From 1b20a6f524383af2da7d7a79f70d1dcd7bf1c6f9 Mon Sep 17 00:00:00 2001 From: Anthony Virtuoso Date: Fri, 15 Nov 2019 11:36:25 -0500 Subject: [PATCH] Initial commit --- .gitignore | 5 + CODE_OF_CONDUCT.md | 4 + CONTRIBUTING.md | 61 + LICENSE | 175 + NOTICE | 1 + README.md | 138 + athena-android/pom.xml | 67 + .../android/AndroidDeviceTable.java | 147 + .../android/AndroidMetadataHandler.java | 109 + .../android/AndroidRecordHandler.java | 174 + .../connectors/android/LiveQueryService.java | 68 + .../connectors/android/QueryRequest.java | 132 + .../connectors/android/QueryResponse.java | 170 + athena-aws-cmdb/LICENSE.txt | 174 + athena-aws-cmdb/README.md | 66 + athena-aws-cmdb/athena-aws-cmdb.yaml | 73 + athena-aws-cmdb/pom.xml | 66 + .../aws/cmdb/AwsCmdbCompositeHandler.java | 35 + .../aws/cmdb/AwsCmdbMetadataHandler.java | 176 + .../aws/cmdb/AwsCmdbRecordHandler.java | 76 + .../aws/cmdb/TableProviderFactory.java | 123 + .../cmdb/tables/EmrClusterTableProvider.java | 205 + .../aws/cmdb/tables/RdsTableProvider.java | 378 + .../aws/cmdb/tables/TableProvider.java | 88 + .../aws/cmdb/tables/ec2/EbsTableProvider.java | 199 + .../aws/cmdb/tables/ec2/Ec2TableProvider.java | 313 + .../cmdb/tables/ec2/ImagesTableProvider.java | 288 + .../cmdb/tables/ec2/RouteTableProvider.java | 215 + .../ec2/SecurityGroupsTableProvider.java | 209 + .../cmdb/tables/ec2/SubnetTableProvider.java | 168 + .../aws/cmdb/tables/ec2/VpcTableProvider.java | 161 + .../tables/s3/S3BucketsTableProvider.java | 133 + .../tables/s3/S3ObjectsTableProvider.java | 166 + .../aws/cmdb/AwsCmdbMetadataHandlerTest.java | 207 + .../aws/cmdb/AwsCmdbRecordHandlerTest.java | 124 + .../aws/cmdb/TableProviderFactoryTest.java | 85 + .../tables/AbstractTableProviderTest.java | 262 + .../tables/EmrClusterTableProviderTest.java | 201 + .../aws/cmdb/tables/RdsTableProviderTest.java | 237 + .../cmdb/tables/ec2/EbsTableProviderTest.java | 181 + .../cmdb/tables/ec2/Ec2TableProviderTest.java | 226 + .../tables/ec2/ImagesTableProviderTest.java | 193 + .../tables/ec2/RouteTableProviderTest.java | 187 + .../ec2/SecurityGroupsTableProviderTest.java | 179 + .../tables/ec2/SubnetTableProviderTest.java | 172 + .../cmdb/tables/ec2/VpcTableProviderTest.java | 171 + .../tables/s3/S3BucketsTableProviderTest.java | 155 + .../tables/s3/S3ObjectsTableProviderTest.java | 185 + athena-bigquery/LICENSE.txt | 174 + athena-bigquery/README.md | 40 + athena-bigquery/athena-bigquery.yaml | 79 + athena-bigquery/pom.xml | 77 + .../bigquery/BigQueryCompositeHandler.java | 35 + .../bigquery/BigQueryConstants.java | 46 + .../bigquery/BigQueryExceptions.java | 33 + .../bigquery/BigQueryMetadataHandler.java | 179 + .../bigquery/BigQueryRecordHandler.java | 181 + .../connectors/bigquery/BigQuerySqlUtils.java | 182 + .../connectors/bigquery/BigQueryUtils.java | 231 + .../bigquery/BigQueryMetadataHandlerTest.java | 184 + .../connectors/bigquery/BigQueryPage.java | 86 + .../bigquery/BigQueryRecordHandlerTest.java | 314 + .../bigquery/BigQuerySqlUtilsTest.java | 115 + .../bigquery/BigQueryTestUtils.java | 143 + athena-cloudwatch-metrics/LICENSE.txt | 174 + athena-cloudwatch-metrics/README.md | 85 + .../athena-cloudwatch-metrics.yaml | 66 + athena-cloudwatch-metrics/pom.xml | 57 + .../cloudwatch/metrics/DimensionSerDe.java | 93 + .../cloudwatch/metrics/MetricUtils.java | 198 + .../metrics/MetricsCompositeHandler.java | 35 + .../metrics/MetricsExceptionFilter.java | 49 + .../metrics/MetricsMetadataHandler.java | 286 + .../metrics/MetricsRecordHandler.java | 262 + .../metrics/tables/MetricSamplesTable.java | 100 + .../metrics/tables/MetricsTable.java | 88 + .../cloudwatch/metrics/tables/Table.java | 53 + .../metrics/DimensionSerDeTest.java | 51 + .../cloudwatch/metrics/MetricUtilsTest.java | 203 + .../metrics/MetricsMetadataHandlerTest.java | 337 + .../metrics/MetricsRecordHandlerTest.java | 343 + .../cloudwatch/metrics/TestUtils.java | 36 + athena-cloudwatch/LICENSE.txt | 174 + athena-cloudwatch/README.md | 60 + athena-cloudwatch/athena-cloudwatch.yaml | 71 + athena-cloudwatch/pom.xml | 57 + .../CloudwatchCompositeHandler.java | 35 + .../cloudwatch/CloudwatchExceptionFilter.java | 45 + .../cloudwatch/CloudwatchMetadataHandler.java | 345 + .../cloudwatch/CloudwatchRecordHandler.java | 172 + .../cloudwatch/CloudwatchTableName.java | 80 + .../cloudwatch/CloudwatchTableResolver.java | 289 + .../CloudwatchMetadataHandlerTest.java | 409 + .../CloudwatchRecordHandlerTest.java | 293 + athena-docdb/LICENSE.txt | 174 + athena-docdb/README.md | 95 + athena-docdb/athena-docdb.yaml | 98 + athena-docdb/pom.xml | 57 + .../docdb/DocDBCompositeHandler.java | 35 + .../docdb/DocDBConnectionFactory.java | 93 + .../connectors/docdb/DocDBFieldResolver.java | 54 + .../docdb/DocDBMetadataHandler.java | 250 + .../connectors/docdb/DocDBRecordHandler.java | 169 + .../athena/connectors/docdb/QueryUtils.java | 247 + .../athena/connectors/docdb/SchemaUtils.java | 158 + .../athena/connectors/docdb/TypeUtils.java | 92 + .../docdb/DocDBConnectionFactoryTest.java | 58 + .../docdb/DocDBMetadataHandlerTest.java | 311 + .../docdb/DocDBRecordHandlerTest.java | 373 + .../connectors/docdb/DocumentGenerator.java | 115 + .../connectors/docdb/StubbingCursor.java | 132 + athena-dynamodb/LICENSE.txt | 174 + athena-dynamodb/README.md | 60 + athena-dynamodb/athena-dynamodb.yaml | 77 + .../libsqlite4java-linux-amd64-1.0.392.so | Bin 0 -> 902282 bytes .../libsqlite4java-linux-i386-1.0.392.so | Bin 0 -> 887104 bytes .../libsqlite4java-osx-1.0.392.dylib | Bin 0 -> 1620880 bytes .../sqlite4java-win32-x64-1.0.392.dll | Bin 0 -> 684032 bytes .../sqlite4java-win32-x86-1.0.392.dll | Bin 0 -> 529408 bytes athena-dynamodb/pom.xml | 99 + .../dynamodb/DynamoDBCompositeHandler.java | 35 + .../dynamodb/DynamoDBMetadataHandler.java | 467 + .../dynamodb/DynamoDBRecordHandler.java | 327 + .../dynamodb/constants/DynamoDBConstants.java | 40 + .../dynamodb/model/DynamoDBTable.java | 122 + .../resolver/DynamoDBTableResolver.java | 163 + .../throttling/DynamoDBExceptionFilter.java | 42 + .../dynamodb/util/DDBPredicateUtils.java | 297 + .../dynamodb/util/DDBTableUtils.java | 216 + .../dynamodb/util/DDBTypeUtils.java | 161 + .../util/IncrementingValueNameProducer.java | 41 + .../dynamodb/DynamoDBMetadataHandlerTest.java | 435 + .../dynamodb/DynamoDBRecordHandlerTest.java | 210 + .../athena/connectors/dynamodb/TestBase.java | 164 + athena-example/LICENSE.txt | 1 + athena-example/README.md | 193 + athena-example/athena-example.yaml | 74 + athena-example/pom.xml | 65 + athena-example/sample_data.csv | 11601 ++++++++++++++++ .../example/ExampleCompositeHandler.java | 35 + .../example/ExampleMetadataHandler.java | 283 + .../athena/example/ExampleRecordHandler.java | 191 + athena-federation-sdk-tools/LICENSE.txt | 174 + athena-federation-sdk-tools/README.md | 9 + athena-federation-sdk-tools/pom.xml | 92 + .../validation/ConnectorValidator.java | 528 + .../validation/ConstraintParser.java | 219 + .../validation/LambdaMetadataProvider.java | 259 + .../validation/LambdaRecordProvider.java | 137 + .../src/main/resources/log4j.properties | 26 + athena-federation-sdk/LICENSE.txt | 174 + athena-federation-sdk/README.md | 185 + .../athena-federation-sdk.yaml | 58 + athena-federation-sdk/checkstyle.xml | 167 + athena-federation-sdk/pom.xml | 235 + .../connector/lambda/CollectionsUtils.java | 45 + .../connector/lambda/QueryStatusChecker.java | 134 + .../connector/lambda/ThrottlingInvoker.java | 278 + .../lambda/data/ArrowTypeComparator.java | 109 + .../athena/connector/lambda/data/Block.java | 518 + .../connector/lambda/data/BlockAllocator.java | 109 + .../lambda/data/BlockAllocatorImpl.java | 287 + .../lambda/data/BlockAllocatorRegistry.java | 41 + .../connector/lambda/data/BlockSpiller.java | 64 + .../connector/lambda/data/BlockUtils.java | 1108 ++ .../connector/lambda/data/BlockWriter.java | 61 + .../connector/lambda/data/FieldBuilder.java | 250 + .../connector/lambda/data/FieldResolver.java | 69 + .../lambda/data/RecordBatchSerDe.java | 90 + .../lambda/data/S3BlockSpillReader.java | 118 + .../connector/lambda/data/S3BlockSpiller.java | 422 + .../connector/lambda/data/SchemaAware.java | 71 + .../connector/lambda/data/SchemaBuilder.java | 304 + .../connector/lambda/data/SchemaSerDe.java | 64 + .../lambda/data/SimpleBlockWriter.java | 71 + .../connector/lambda/data/SpillConfig.java | 180 + .../data/projectors/ArrowValueProjector.java | 36 + .../projectors/ArrowValueProjectorImpl.java | 133 + .../projectors/ListArrowValueProjector.java | 78 + .../data/projectors/ProjectorUtils.java | 42 + .../projectors/SimpleArrowValueProjector.java | 45 + .../projectors/StructArrowValueProjector.java | 81 + .../lambda/data/writers/ArrowValueWriter.java | 36 + .../data/writers/ComplexArrowValueWriter.java | 47 + .../data/writers/SimpleArrowValueWriter.java | 312 + .../lambda/data/writers/WriterUtils.java | 42 + .../athena/connector/lambda/domain/Split.java | 241 + .../connector/lambda/domain/TableName.java | 106 + .../domain/predicate/AllOrNoneValueSet.java | 202 + .../domain/predicate/ConstraintEvaluator.java | 105 + .../lambda/domain/predicate/Constraints.java | 87 + .../domain/predicate/EquatableValueSet.java | 458 + .../lambda/domain/predicate/Marker.java | 368 + .../domain/predicate/MarkerFactory.java | 151 + .../lambda/domain/predicate/Range.java | 231 + .../lambda/domain/predicate/Ranges.java | 38 + .../domain/predicate/SortedRangeSet.java | 505 + .../lambda/domain/predicate/ValueSet.java | 99 + .../lambda/domain/spill/S3SpillLocation.java | 176 + .../lambda/domain/spill/SpillLocation.java | 37 + .../lambda/examples/ContinuationToken.java | 104 + .../examples/ExampleCompositeHandler.java | 36 + .../examples/ExampleMetadataHandler.java | 449 + .../lambda/examples/ExampleRecordHandler.java | 309 + .../ExampleUserDefinedFunctionHandler.java | 118 + .../lambda/examples/SplitProperties.java | 40 + .../FederationThrottleException.java | 54 + .../handlers/AthenaExceptionFilter.java | 37 + .../lambda/handlers/CompositeHandler.java | 133 + .../handlers/FederationCapabilities.java | 34 + .../lambda/handlers/GlueMetadataHandler.java | 330 + .../lambda/handlers/MetadataHandler.java | 457 + .../lambda/handlers/RecordHandler.java | 260 + .../lambda/metadata/GetSplitsRequest.java | 174 + .../lambda/metadata/GetSplitsResponse.java | 122 + .../metadata/GetTableLayoutRequest.java | 130 + .../metadata/GetTableLayoutResponse.java | 102 + .../lambda/metadata/GetTableRequest.java | 90 + .../lambda/metadata/GetTableResponse.java | 120 + .../lambda/metadata/ListSchemasRequest.java | 73 + .../lambda/metadata/ListSchemasResponse.java | 89 + .../lambda/metadata/ListTablesRequest.java | 90 + .../lambda/metadata/ListTablesResponse.java | 90 + .../lambda/metadata/MetadataRequest.java | 59 + .../lambda/metadata/MetadataRequestType.java | 30 + .../lambda/metadata/MetadataResponse.java | 50 + .../lambda/metadata/MetadataService.java | 29 + .../lambda/metadata/glue/DefaultGlueType.java | 82 + .../lambda/metadata/glue/GlueFieldLexer.java | 125 + .../lambda/metadata/glue/GlueTypeParser.java | 153 + .../lambda/records/ReadRecordsRequest.java | 155 + .../lambda/records/ReadRecordsResponse.java | 105 + .../lambda/records/RecordRequest.java | 60 + .../lambda/records/RecordRequestType.java | 26 + .../lambda/records/RecordResponse.java | 50 + .../lambda/records/RecordService.java | 29 + .../records/RemoteReadRecordsResponse.java | 125 + .../lambda/request/FederationRequest.java | 66 + .../lambda/request/FederationResponse.java | 50 + .../connector/lambda/request/PingRequest.java | 74 + .../lambda/request/PingResponse.java | 91 + .../lambda/security/AesGcmBlockCrypto.java | 131 + .../lambda/security/BlockCrypto.java | 33 + .../security/CachableSecretsManager.java | 152 + .../lambda/security/EncryptionKey.java | 73 + .../lambda/security/EncryptionKeyFactory.java | 29 + .../lambda/security/FederatedIdentity.java | 59 + .../lambda/security/KmsKeyFactory.java | 56 + .../lambda/security/LocalKeyFactory.java | 47 + .../lambda/security/NoOpBlockCrypto.java | 74 + .../lambda/serde/BlockDeserializer.java | 110 + .../lambda/serde/BlockSerializer.java | 70 + .../lambda/serde/ObjectMapperFactory.java | 51 + .../lambda/serde/SchemaDeserializer.java | 52 + .../lambda/serde/SchemaSerializer.java | 53 + .../udf/UserDefinedFunctionHandler.java | 227 + .../udf/UserDefinedFunctionRequest.java | 105 + .../udf/UserDefinedFunctionResponse.java | 59 + .../lambda/udf/UserDefinedFunctionType.java | 25 + .../src/main/resources/log4j.properties | 26 + .../lambda/QueryStatusCheckerTest.java | 119 + .../lambda/ThrottlingInvokerTest.java | 122 + .../connector/lambda/data/BlockTest.java | 858 ++ .../connector/lambda/data/BlockUtilsTest.java | 104 + .../lambda/data/S3BlockSpillerTest.java | 207 + .../lambda/data/UnitTestBlockUtils.java | 147 + .../predicate/AllOrNoneValueSetTest.java | 177 + .../predicate/EquatableValueSetTest.java | 328 + .../lambda/domain/predicate/MarkerTest.java | 176 + .../lambda/domain/predicate/RangeTest.java | 297 + .../domain/predicate/SortedRangeSetTest.java | 457 + .../examples/ExampleMetadataHandlerTest.java | 326 + .../examples/ExampleRecordHandlerTest.java | 340 + .../lambda/handlers/CompositeHandlerTest.java | 229 + .../handlers/GlueMetadataHandlerTest.java | 285 + .../metadata/glue/GlueFieldLexerTest.java | 108 + .../metadata/glue/GlueTypeParserTest.java | 73 + .../lambda/security/BlockCryptoTest.java | 74 + .../security/CacheableSecretsManagerTest.java | 134 + .../lambda/security/IdentityUtil.java | 31 + .../lambda/serde/BlockSerializationTest.java | 83 + .../serde/ConstraintSerializationTest.java | 96 + .../lambda/serde/MarkerSerializationTest.java | 107 + .../lambda/serde/ObjectMapperUtil.java | 50 + .../lambda/serde/SchemaSerializationTest.java | 78 + .../connector/lambda/serde/TestPojo.java | 41 + .../udf/UserDefinedFunctionHandlerTest.java | 357 + .../src/test/resources/log4j.properties | 26 + athena-hbase/LICENSE.txt | 174 + athena-hbase/README.md | 87 + athena-hbase/athena-hbase.yaml | 97 + athena-hbase/pom.xml | 76 + .../hbase/HbaseCompositeHandler.java | 35 + .../hbase/HbaseConnectionFactory.java | 156 + .../connectors/hbase/HbaseFieldResolver.java | 74 + .../hbase/HbaseMetadataHandler.java | 291 + .../connectors/hbase/HbaseRecordHandler.java | 248 + .../connectors/hbase/HbaseSchemaUtils.java | 277 + .../src/main/resources/sample_data.hbase | 106 + .../hbase/HbaseConnectionFactoryTest.java | 62 + .../hbase/HbaseFieldResolverTest.java | 49 + .../hbase/HbaseMetadataHandlerTest.java | 301 + .../hbase/HbaseRecordHandlerTest.java | 304 + .../hbase/HbaseSchemaUtilsTest.java | 175 + .../athena/connectors/hbase/TestUtils.java | 162 + athena-jdbc/LICENSE.txt | 174 + athena-jdbc/README.md | 176 + athena-jdbc/athena-jdbc.yaml | 102 + athena-jdbc/pom.xml | 92 + .../MultiplexingJdbcCompositeHandler.java | 35 + .../jdbc/MultiplexingJdbcMetadataHandler.java | 137 + .../jdbc/MultiplexingJdbcRecordHandler.java | 92 + .../connection/DatabaseConnectionConfig.java | 100 + .../DatabaseConnectionConfigBuilder.java | 145 + .../connection/DatabaseConnectionInfo.java | 66 + .../GenericJdbcConnectionFactory.java | 118 + .../connection/JdbcConnectionFactory.java | 60 + .../jdbc/connection/JdbcCredential.java | 69 + .../connection/JdbcCredentialProvider.java | 33 + .../RdsSecretsCredentialProvider.java | 56 + .../StaticJdbcCredentialProvider.java | 44 + .../athena/jdbc/manager/JDBCUtil.java | 151 + .../jdbc/manager/JdbcArrowTypeConverter.java | 40 + .../jdbc/manager/JdbcMetadataHandler.java | 260 + .../jdbc/manager/JdbcRecordHandler.java | 183 + .../jdbc/manager/JdbcSplitQueryBuilder.java | 289 + .../manager/PreparedStatementBuilder.java | 79 + .../jdbc/mysql/MySqlCompositeHandler.java | 35 + .../jdbc/mysql/MySqlMetadataHandler.java | 190 + .../jdbc/mysql/MySqlQueryStringBuilder.java | 55 + .../athena/jdbc/mysql/MySqlRecordHandler.java | 89 + .../PostGreSqlCompositeHandler.java | 37 + .../postgresql/PostGreSqlMetadataHandler.java | 190 + .../PostGreSqlQueryStringBuilder.java | 56 + .../postgresql/PostGreSqlRecordHandler.java | 88 + .../MultiplexingJdbcMetadataHandlerTest.java | 143 + .../MultiplexingJdbcRecordHandlerTest.java | 104 + .../connectors/athena/jdbc/TestBase.java | 91 + .../DatabaseConnectionConfigBuilderTest.java | 72 + .../JdbcCredentialProviderTest.java | 53 + .../athena/jdbc/manager/JDBCUtilTest.java | 101 + .../manager/JdbcArrowTypeConverterTest.java | 55 + .../jdbc/manager/JdbcMetadataHandlerTest.java | 227 + .../jdbc/manager/JdbcRecordHandlerTest.java | 151 + .../jdbc/mysql/MySqlMetadataHandlerTest.java | 273 + .../jdbc/mysql/MySqlRecordHandlerTest.java | 170 + .../PostGreSqlMetadataHandlerTest.java | 278 + .../PostGreSqlRecordHandlerTest.java | 172 + athena-redis/LICENSE.txt | 174 + athena-redis/README.md | 70 + athena-redis/athena-redis.yaml | 93 + athena-redis/pom.xml | 61 + .../connectors/redis/JedisPoolFactory.java | 87 + .../athena/connectors/redis/KeyType.java | 74 + .../redis/RedisCompositeHandler.java | 35 + .../redis/RedisMetadataHandler.java | 367 + .../connectors/redis/RedisRecordHandler.java | 259 + .../connectors/redis/ValueConverter.java | 77 + .../athena/connectors/redis/ValueType.java | 74 + .../redis/RedisMetadataHandlerTest.java | 279 + .../redis/RedisRecordHandlerTest.java | 475 + athena-tpcds/LICENSE.txt | 175 + athena-tpcds/README.md | 135 + athena-tpcds/athena-tpcds.yaml | 64 + athena-tpcds/pom.xml | 57 + .../tpcds/TPCDSCompositeHandler.java | 31 + .../tpcds/TPCDSMetadataHandler.java | 199 + .../connectors/tpcds/TPCDSRecordHandler.java | 229 + .../athena/connectors/tpcds/TPCDSUtils.java | 106 + .../src/main/resources/queries/q1.sql | 19 + .../src/main/resources/queries/q10.sql | 57 + .../src/main/resources/queries/q11.sql | 68 + .../src/main/resources/queries/q12.sql | 22 + .../src/main/resources/queries/q13.sql | 49 + .../src/main/resources/queries/q14a.sql | 120 + .../src/main/resources/queries/q14b.sql | 95 + .../src/main/resources/queries/q15.sql | 15 + .../src/main/resources/queries/q16.sql | 23 + .../src/main/resources/queries/q17.sql | 33 + .../src/main/resources/queries/q18.sql | 28 + .../src/main/resources/queries/q19.sql | 19 + .../src/main/resources/queries/q2.sql | 81 + .../src/main/resources/queries/q20.sql | 18 + .../src/main/resources/queries/q21.sql | 25 + .../src/main/resources/queries/q22.sql | 14 + .../src/main/resources/queries/q23a.sql | 53 + .../src/main/resources/queries/q23b.sql | 67 + .../src/main/resources/queries/q24a.sql | 34 + .../src/main/resources/queries/q24b.sql | 34 + .../src/main/resources/queries/q25.sql | 33 + .../src/main/resources/queries/q26.sql | 19 + .../src/main/resources/queries/q27.sql | 21 + .../src/main/resources/queries/q28.sql | 56 + .../src/main/resources/queries/q29.sql | 32 + .../src/main/resources/queries/q3.sql | 13 + .../src/main/resources/queries/q30.sql | 35 + .../src/main/resources/queries/q31.sql | 60 + .../src/main/resources/queries/q32.sql | 15 + .../src/main/resources/queries/q33.sql | 65 + .../src/main/resources/queries/q34.sql | 32 + .../src/main/resources/queries/q35.sql | 46 + .../src/main/resources/queries/q37.sql | 15 + .../src/main/resources/queries/q38.sql | 30 + .../src/main/resources/queries/q39a.sql | 47 + .../src/main/resources/queries/q39b.sql | 49 + .../src/main/resources/queries/q4.sql | 120 + .../src/main/resources/queries/q40.sql | 25 + .../src/main/resources/queries/q41.sql | 49 + .../src/main/resources/queries/q42.sql | 18 + .../src/main/resources/queries/q43.sql | 33 + .../src/main/resources/queries/q44.sql | 46 + .../src/main/resources/queries/q45.sql | 21 + .../src/main/resources/queries/q46.sql | 32 + .../src/main/resources/queries/q47.sql | 63 + .../src/main/resources/queries/q48.sql | 63 + .../src/main/resources/queries/q49.sql | 126 + .../src/main/resources/queries/q5.sql | 131 + .../src/main/resources/queries/q50.sql | 47 + .../src/main/resources/queries/q51.sql | 55 + .../src/main/resources/queries/q52.sql | 14 + .../src/main/resources/queries/q53.sql | 30 + .../src/main/resources/queries/q54.sql | 61 + .../src/main/resources/queries/q55.sql | 13 + .../src/main/resources/queries/q56.sql | 65 + .../src/main/resources/queries/q57.sql | 56 + .../src/main/resources/queries/q59.sql | 75 + .../src/main/resources/queries/q6.sql | 21 + .../src/main/resources/queries/q60.sql | 62 + .../src/main/resources/queries/q61.sql | 33 + .../src/main/resources/queries/q62.sql | 35 + .../src/main/resources/queries/q63.sql | 31 + .../src/main/resources/queries/q64.sql | 92 + .../src/main/resources/queries/q65.sql | 33 + .../src/main/resources/queries/q66.sql | 240 + .../src/main/resources/queries/q67.sql | 38 + .../src/main/resources/queries/q68.sql | 34 + .../src/main/resources/queries/q69.sql | 38 + .../src/main/resources/queries/q7.sql | 19 + .../src/main/resources/queries/q71.sql | 44 + .../src/main/resources/queries/q73.sql | 30 + .../src/main/resources/queries/q74.sql | 58 + .../src/main/resources/queries/q75.sql | 76 + .../src/main/resources/queries/q76.sql | 47 + .../src/main/resources/queries/q77.sql | 100 + .../src/main/resources/queries/q78.sql | 64 + .../src/main/resources/queries/q79.sql | 27 + .../src/main/resources/queries/q8.sql | 87 + .../src/main/resources/queries/q80.sql | 94 + .../src/main/resources/queries/q81.sql | 38 + .../src/main/resources/queries/q82.sql | 15 + .../src/main/resources/queries/q83.sql | 56 + .../src/main/resources/queries/q84.sql | 19 + .../src/main/resources/queries/q85.sql | 82 + .../src/main/resources/queries/q87.sql | 28 + .../src/main/resources/queries/q88.sql | 122 + .../src/main/resources/queries/q89.sql | 30 + .../src/main/resources/queries/q9.sql | 48 + .../src/main/resources/queries/q90.sql | 19 + .../src/main/resources/queries/q91.sql | 23 + .../src/main/resources/queries/q92.sql | 16 + .../src/main/resources/queries/q93.sql | 19 + .../src/main/resources/queries/q94.sql | 23 + .../src/main/resources/queries/q95.sql | 29 + .../src/main/resources/queries/q96.sql | 11 + .../src/main/resources/queries/q97.sql | 30 + .../src/main/resources/queries/q98.sql | 21 + .../src/main/resources/queries/q99.sql | 34 + .../tpcds/TPCDSMetadataHandlerTest.java | 224 + .../tpcds/TPCDSRecordHandlerTest.java | 277 + athena-udfs/LICENSE.txt | 175 + athena-udfs/README.md | 22 + athena-udfs/athena-udfs.yaml | 40 + athena-udfs/pom.xml | 57 + .../connectors/udfs/AthenaUDFHandler.java | 117 + .../connectors/udfs/AthenaUDFHandlerTest.java | 27 + checkstyle.xml | 171 + docs/img/athena_federation_demo.png | Bin 0 -> 147728 bytes docs/img/athena_federation_flow.png | Bin 0 -> 33218 bytes docs/img/athena_federation_summary.png | Bin 0 -> 58652 bytes docs/img/hbase_glue_example.png | Bin 0 -> 190985 bytes pom.xml | 187 + tools/prepare_dev_env.sh | 74 + tools/publish.sh | 97 + tools/validate_connector.sh | 50 + 484 files changed, 71026 insertions(+) create mode 100644 .gitignore create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE create mode 100644 NOTICE create mode 100644 README.md create mode 100644 athena-android/pom.xml create mode 100644 athena-android/src/main/java/com/amazonaws/athena/connectors/android/AndroidDeviceTable.java create mode 100644 athena-android/src/main/java/com/amazonaws/athena/connectors/android/AndroidMetadataHandler.java create mode 100644 athena-android/src/main/java/com/amazonaws/athena/connectors/android/AndroidRecordHandler.java create mode 100644 athena-android/src/main/java/com/amazonaws/athena/connectors/android/LiveQueryService.java create mode 100644 athena-android/src/main/java/com/amazonaws/athena/connectors/android/QueryRequest.java create mode 100644 athena-android/src/main/java/com/amazonaws/athena/connectors/android/QueryResponse.java create mode 100644 athena-aws-cmdb/LICENSE.txt create mode 100644 athena-aws-cmdb/README.md create mode 100644 athena-aws-cmdb/athena-aws-cmdb.yaml create mode 100644 athena-aws-cmdb/pom.xml create mode 100644 athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/AwsCmdbCompositeHandler.java create mode 100644 athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/AwsCmdbMetadataHandler.java create mode 100644 athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/AwsCmdbRecordHandler.java create mode 100644 athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/TableProviderFactory.java create mode 100644 athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/EmrClusterTableProvider.java create mode 100644 athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/RdsTableProvider.java create mode 100644 athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/TableProvider.java create mode 100644 athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/EbsTableProvider.java create mode 100644 athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/Ec2TableProvider.java create mode 100644 athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/ImagesTableProvider.java create mode 100644 athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/RouteTableProvider.java create mode 100644 athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/SecurityGroupsTableProvider.java create mode 100644 athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/SubnetTableProvider.java create mode 100644 athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/VpcTableProvider.java create mode 100644 athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/s3/S3BucketsTableProvider.java create mode 100644 athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/s3/S3ObjectsTableProvider.java create mode 100644 athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/AwsCmdbMetadataHandlerTest.java create mode 100644 athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/AwsCmdbRecordHandlerTest.java create mode 100644 athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/TableProviderFactoryTest.java create mode 100644 athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/AbstractTableProviderTest.java create mode 100644 athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/EmrClusterTableProviderTest.java create mode 100644 athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/RdsTableProviderTest.java create mode 100644 athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/EbsTableProviderTest.java create mode 100644 athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/Ec2TableProviderTest.java create mode 100644 athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/ImagesTableProviderTest.java create mode 100644 athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/RouteTableProviderTest.java create mode 100644 athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/SecurityGroupsTableProviderTest.java create mode 100644 athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/SubnetTableProviderTest.java create mode 100644 athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/VpcTableProviderTest.java create mode 100644 athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/s3/S3BucketsTableProviderTest.java create mode 100644 athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/s3/S3ObjectsTableProviderTest.java create mode 100644 athena-bigquery/LICENSE.txt create mode 100644 athena-bigquery/README.md create mode 100644 athena-bigquery/athena-bigquery.yaml create mode 100644 athena-bigquery/pom.xml create mode 100644 athena-bigquery/src/main/java/com/amazonaws/athena/connectors/bigquery/BigQueryCompositeHandler.java create mode 100644 athena-bigquery/src/main/java/com/amazonaws/athena/connectors/bigquery/BigQueryConstants.java create mode 100644 athena-bigquery/src/main/java/com/amazonaws/athena/connectors/bigquery/BigQueryExceptions.java create mode 100644 athena-bigquery/src/main/java/com/amazonaws/athena/connectors/bigquery/BigQueryMetadataHandler.java create mode 100644 athena-bigquery/src/main/java/com/amazonaws/athena/connectors/bigquery/BigQueryRecordHandler.java create mode 100644 athena-bigquery/src/main/java/com/amazonaws/athena/connectors/bigquery/BigQuerySqlUtils.java create mode 100644 athena-bigquery/src/main/java/com/amazonaws/athena/connectors/bigquery/BigQueryUtils.java create mode 100644 athena-bigquery/src/test/java/com/amazonaws/athena/connectors/bigquery/BigQueryMetadataHandlerTest.java create mode 100644 athena-bigquery/src/test/java/com/amazonaws/athena/connectors/bigquery/BigQueryPage.java create mode 100644 athena-bigquery/src/test/java/com/amazonaws/athena/connectors/bigquery/BigQueryRecordHandlerTest.java create mode 100644 athena-bigquery/src/test/java/com/amazonaws/athena/connectors/bigquery/BigQuerySqlUtilsTest.java create mode 100644 athena-bigquery/src/test/java/com/amazonaws/athena/connectors/bigquery/BigQueryTestUtils.java create mode 100644 athena-cloudwatch-metrics/LICENSE.txt create mode 100644 athena-cloudwatch-metrics/README.md create mode 100644 athena-cloudwatch-metrics/athena-cloudwatch-metrics.yaml create mode 100644 athena-cloudwatch-metrics/pom.xml create mode 100644 athena-cloudwatch-metrics/src/main/java/com/amazonaws/athena/connectors/cloudwatch/metrics/DimensionSerDe.java create mode 100644 athena-cloudwatch-metrics/src/main/java/com/amazonaws/athena/connectors/cloudwatch/metrics/MetricUtils.java create mode 100644 athena-cloudwatch-metrics/src/main/java/com/amazonaws/athena/connectors/cloudwatch/metrics/MetricsCompositeHandler.java create mode 100644 athena-cloudwatch-metrics/src/main/java/com/amazonaws/athena/connectors/cloudwatch/metrics/MetricsExceptionFilter.java create mode 100644 athena-cloudwatch-metrics/src/main/java/com/amazonaws/athena/connectors/cloudwatch/metrics/MetricsMetadataHandler.java create mode 100644 athena-cloudwatch-metrics/src/main/java/com/amazonaws/athena/connectors/cloudwatch/metrics/MetricsRecordHandler.java create mode 100644 athena-cloudwatch-metrics/src/main/java/com/amazonaws/athena/connectors/cloudwatch/metrics/tables/MetricSamplesTable.java create mode 100644 athena-cloudwatch-metrics/src/main/java/com/amazonaws/athena/connectors/cloudwatch/metrics/tables/MetricsTable.java create mode 100644 athena-cloudwatch-metrics/src/main/java/com/amazonaws/athena/connectors/cloudwatch/metrics/tables/Table.java create mode 100644 athena-cloudwatch-metrics/src/test/java/com/amazonaws/athena/connectors/cloudwatch/metrics/DimensionSerDeTest.java create mode 100644 athena-cloudwatch-metrics/src/test/java/com/amazonaws/athena/connectors/cloudwatch/metrics/MetricUtilsTest.java create mode 100644 athena-cloudwatch-metrics/src/test/java/com/amazonaws/athena/connectors/cloudwatch/metrics/MetricsMetadataHandlerTest.java create mode 100644 athena-cloudwatch-metrics/src/test/java/com/amazonaws/athena/connectors/cloudwatch/metrics/MetricsRecordHandlerTest.java create mode 100644 athena-cloudwatch-metrics/src/test/java/com/amazonaws/athena/connectors/cloudwatch/metrics/TestUtils.java create mode 100644 athena-cloudwatch/LICENSE.txt create mode 100644 athena-cloudwatch/README.md create mode 100644 athena-cloudwatch/athena-cloudwatch.yaml create mode 100644 athena-cloudwatch/pom.xml create mode 100644 athena-cloudwatch/src/main/java/com/amazonaws/athena/connectors/cloudwatch/CloudwatchCompositeHandler.java create mode 100644 athena-cloudwatch/src/main/java/com/amazonaws/athena/connectors/cloudwatch/CloudwatchExceptionFilter.java create mode 100644 athena-cloudwatch/src/main/java/com/amazonaws/athena/connectors/cloudwatch/CloudwatchMetadataHandler.java create mode 100644 athena-cloudwatch/src/main/java/com/amazonaws/athena/connectors/cloudwatch/CloudwatchRecordHandler.java create mode 100644 athena-cloudwatch/src/main/java/com/amazonaws/athena/connectors/cloudwatch/CloudwatchTableName.java create mode 100644 athena-cloudwatch/src/main/java/com/amazonaws/athena/connectors/cloudwatch/CloudwatchTableResolver.java create mode 100644 athena-cloudwatch/src/test/java/com/amazonaws/athena/connectors/cloudwatch/CloudwatchMetadataHandlerTest.java create mode 100644 athena-cloudwatch/src/test/java/com/amazonaws/athena/connectors/cloudwatch/CloudwatchRecordHandlerTest.java create mode 100644 athena-docdb/LICENSE.txt create mode 100644 athena-docdb/README.md create mode 100644 athena-docdb/athena-docdb.yaml create mode 100644 athena-docdb/pom.xml create mode 100644 athena-docdb/src/main/java/com/amazonaws/athena/connectors/docdb/DocDBCompositeHandler.java create mode 100644 athena-docdb/src/main/java/com/amazonaws/athena/connectors/docdb/DocDBConnectionFactory.java create mode 100644 athena-docdb/src/main/java/com/amazonaws/athena/connectors/docdb/DocDBFieldResolver.java create mode 100644 athena-docdb/src/main/java/com/amazonaws/athena/connectors/docdb/DocDBMetadataHandler.java create mode 100644 athena-docdb/src/main/java/com/amazonaws/athena/connectors/docdb/DocDBRecordHandler.java create mode 100644 athena-docdb/src/main/java/com/amazonaws/athena/connectors/docdb/QueryUtils.java create mode 100644 athena-docdb/src/main/java/com/amazonaws/athena/connectors/docdb/SchemaUtils.java create mode 100644 athena-docdb/src/main/java/com/amazonaws/athena/connectors/docdb/TypeUtils.java create mode 100644 athena-docdb/src/test/java/com/amazonaws/athena/connectors/docdb/DocDBConnectionFactoryTest.java create mode 100644 athena-docdb/src/test/java/com/amazonaws/athena/connectors/docdb/DocDBMetadataHandlerTest.java create mode 100644 athena-docdb/src/test/java/com/amazonaws/athena/connectors/docdb/DocDBRecordHandlerTest.java create mode 100644 athena-docdb/src/test/java/com/amazonaws/athena/connectors/docdb/DocumentGenerator.java create mode 100644 athena-docdb/src/test/java/com/amazonaws/athena/connectors/docdb/StubbingCursor.java create mode 100644 athena-dynamodb/LICENSE.txt create mode 100644 athena-dynamodb/README.md create mode 100644 athena-dynamodb/athena-dynamodb.yaml create mode 100644 athena-dynamodb/native-libs/libsqlite4java-linux-amd64-1.0.392.so create mode 100644 athena-dynamodb/native-libs/libsqlite4java-linux-i386-1.0.392.so create mode 100644 athena-dynamodb/native-libs/libsqlite4java-osx-1.0.392.dylib create mode 100644 athena-dynamodb/native-libs/sqlite4java-win32-x64-1.0.392.dll create mode 100644 athena-dynamodb/native-libs/sqlite4java-win32-x86-1.0.392.dll create mode 100644 athena-dynamodb/pom.xml create mode 100644 athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/DynamoDBCompositeHandler.java create mode 100644 athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/DynamoDBMetadataHandler.java create mode 100644 athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/DynamoDBRecordHandler.java create mode 100644 athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/constants/DynamoDBConstants.java create mode 100644 athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/model/DynamoDBTable.java create mode 100644 athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/resolver/DynamoDBTableResolver.java create mode 100644 athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/throttling/DynamoDBExceptionFilter.java create mode 100644 athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/util/DDBPredicateUtils.java create mode 100644 athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/util/DDBTableUtils.java create mode 100644 athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/util/DDBTypeUtils.java create mode 100644 athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/util/IncrementingValueNameProducer.java create mode 100644 athena-dynamodb/src/test/java/com/amazonaws/athena/connectors/dynamodb/DynamoDBMetadataHandlerTest.java create mode 100644 athena-dynamodb/src/test/java/com/amazonaws/athena/connectors/dynamodb/DynamoDBRecordHandlerTest.java create mode 100644 athena-dynamodb/src/test/java/com/amazonaws/athena/connectors/dynamodb/TestBase.java create mode 100644 athena-example/LICENSE.txt create mode 100644 athena-example/README.md create mode 100644 athena-example/athena-example.yaml create mode 100644 athena-example/pom.xml create mode 100644 athena-example/sample_data.csv create mode 100644 athena-example/src/main/java/com/amazonaws/connectors/athena/example/ExampleCompositeHandler.java create mode 100644 athena-example/src/main/java/com/amazonaws/connectors/athena/example/ExampleMetadataHandler.java create mode 100644 athena-example/src/main/java/com/amazonaws/connectors/athena/example/ExampleRecordHandler.java create mode 100644 athena-federation-sdk-tools/LICENSE.txt create mode 100644 athena-federation-sdk-tools/README.md create mode 100644 athena-federation-sdk-tools/pom.xml create mode 100644 athena-federation-sdk-tools/src/main/java/com/amazonaws/athena/connector/validation/ConnectorValidator.java create mode 100644 athena-federation-sdk-tools/src/main/java/com/amazonaws/athena/connector/validation/ConstraintParser.java create mode 100644 athena-federation-sdk-tools/src/main/java/com/amazonaws/athena/connector/validation/LambdaMetadataProvider.java create mode 100644 athena-federation-sdk-tools/src/main/java/com/amazonaws/athena/connector/validation/LambdaRecordProvider.java create mode 100644 athena-federation-sdk-tools/src/main/resources/log4j.properties create mode 100644 athena-federation-sdk/LICENSE.txt create mode 100644 athena-federation-sdk/README.md create mode 100644 athena-federation-sdk/athena-federation-sdk.yaml create mode 100644 athena-federation-sdk/checkstyle.xml create mode 100644 athena-federation-sdk/pom.xml create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/CollectionsUtils.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/QueryStatusChecker.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/ThrottlingInvoker.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/ArrowTypeComparator.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/Block.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/BlockAllocator.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/BlockAllocatorImpl.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/BlockAllocatorRegistry.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/BlockSpiller.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/BlockUtils.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/BlockWriter.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/FieldBuilder.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/FieldResolver.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/RecordBatchSerDe.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/S3BlockSpillReader.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/S3BlockSpiller.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/SchemaAware.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/SchemaBuilder.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/SchemaSerDe.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/SimpleBlockWriter.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/SpillConfig.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/projectors/ArrowValueProjector.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/projectors/ArrowValueProjectorImpl.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/projectors/ListArrowValueProjector.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/projectors/ProjectorUtils.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/projectors/SimpleArrowValueProjector.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/projectors/StructArrowValueProjector.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/writers/ArrowValueWriter.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/writers/ComplexArrowValueWriter.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/writers/SimpleArrowValueWriter.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/writers/WriterUtils.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/Split.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/TableName.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/AllOrNoneValueSet.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/ConstraintEvaluator.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/Constraints.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/EquatableValueSet.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/Marker.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/MarkerFactory.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/Range.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/Ranges.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/SortedRangeSet.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/ValueSet.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/spill/S3SpillLocation.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/spill/SpillLocation.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/examples/ContinuationToken.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/examples/ExampleCompositeHandler.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/examples/ExampleMetadataHandler.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/examples/ExampleRecordHandler.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/examples/ExampleUserDefinedFunctionHandler.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/examples/SplitProperties.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/exceptions/FederationThrottleException.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/handlers/AthenaExceptionFilter.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/handlers/CompositeHandler.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/handlers/FederationCapabilities.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/handlers/GlueMetadataHandler.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/handlers/MetadataHandler.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/handlers/RecordHandler.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/GetSplitsRequest.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/GetSplitsResponse.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/GetTableLayoutRequest.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/GetTableLayoutResponse.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/GetTableRequest.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/GetTableResponse.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/ListSchemasRequest.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/ListSchemasResponse.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/ListTablesRequest.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/ListTablesResponse.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/MetadataRequest.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/MetadataRequestType.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/MetadataResponse.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/MetadataService.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/glue/DefaultGlueType.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/glue/GlueFieldLexer.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/glue/GlueTypeParser.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/records/ReadRecordsRequest.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/records/ReadRecordsResponse.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/records/RecordRequest.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/records/RecordRequestType.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/records/RecordResponse.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/records/RecordService.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/records/RemoteReadRecordsResponse.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/request/FederationRequest.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/request/FederationResponse.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/request/PingRequest.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/request/PingResponse.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/security/AesGcmBlockCrypto.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/security/BlockCrypto.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/security/CachableSecretsManager.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/security/EncryptionKey.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/security/EncryptionKeyFactory.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/security/FederatedIdentity.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/security/KmsKeyFactory.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/security/LocalKeyFactory.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/security/NoOpBlockCrypto.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/serde/BlockDeserializer.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/serde/BlockSerializer.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/serde/ObjectMapperFactory.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/serde/SchemaDeserializer.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/serde/SchemaSerializer.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/udf/UserDefinedFunctionHandler.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/udf/UserDefinedFunctionRequest.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/udf/UserDefinedFunctionResponse.java create mode 100644 athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/udf/UserDefinedFunctionType.java create mode 100644 athena-federation-sdk/src/main/resources/log4j.properties create mode 100644 athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/QueryStatusCheckerTest.java create mode 100644 athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/ThrottlingInvokerTest.java create mode 100644 athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/data/BlockTest.java create mode 100644 athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/data/BlockUtilsTest.java create mode 100644 athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/data/S3BlockSpillerTest.java create mode 100644 athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/data/UnitTestBlockUtils.java create mode 100644 athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/domain/predicate/AllOrNoneValueSetTest.java create mode 100644 athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/domain/predicate/EquatableValueSetTest.java create mode 100644 athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/domain/predicate/MarkerTest.java create mode 100644 athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/domain/predicate/RangeTest.java create mode 100644 athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/domain/predicate/SortedRangeSetTest.java create mode 100644 athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/examples/ExampleMetadataHandlerTest.java create mode 100644 athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/examples/ExampleRecordHandlerTest.java create mode 100644 athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/handlers/CompositeHandlerTest.java create mode 100644 athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/handlers/GlueMetadataHandlerTest.java create mode 100644 athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/metadata/glue/GlueFieldLexerTest.java create mode 100644 athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/metadata/glue/GlueTypeParserTest.java create mode 100644 athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/security/BlockCryptoTest.java create mode 100644 athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/security/CacheableSecretsManagerTest.java create mode 100644 athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/security/IdentityUtil.java create mode 100644 athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/serde/BlockSerializationTest.java create mode 100644 athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/serde/ConstraintSerializationTest.java create mode 100644 athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/serde/MarkerSerializationTest.java create mode 100644 athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/serde/ObjectMapperUtil.java create mode 100644 athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/serde/SchemaSerializationTest.java create mode 100644 athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/serde/TestPojo.java create mode 100644 athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/udf/UserDefinedFunctionHandlerTest.java create mode 100644 athena-federation-sdk/src/test/resources/log4j.properties create mode 100644 athena-hbase/LICENSE.txt create mode 100644 athena-hbase/README.md create mode 100644 athena-hbase/athena-hbase.yaml create mode 100644 athena-hbase/pom.xml create mode 100644 athena-hbase/src/main/java/com/amazonaws/athena/connectors/hbase/HbaseCompositeHandler.java create mode 100644 athena-hbase/src/main/java/com/amazonaws/athena/connectors/hbase/HbaseConnectionFactory.java create mode 100644 athena-hbase/src/main/java/com/amazonaws/athena/connectors/hbase/HbaseFieldResolver.java create mode 100644 athena-hbase/src/main/java/com/amazonaws/athena/connectors/hbase/HbaseMetadataHandler.java create mode 100644 athena-hbase/src/main/java/com/amazonaws/athena/connectors/hbase/HbaseRecordHandler.java create mode 100644 athena-hbase/src/main/java/com/amazonaws/athena/connectors/hbase/HbaseSchemaUtils.java create mode 100644 athena-hbase/src/main/resources/sample_data.hbase create mode 100644 athena-hbase/src/test/java/com/amazonaws/athena/connectors/hbase/HbaseConnectionFactoryTest.java create mode 100644 athena-hbase/src/test/java/com/amazonaws/athena/connectors/hbase/HbaseFieldResolverTest.java create mode 100644 athena-hbase/src/test/java/com/amazonaws/athena/connectors/hbase/HbaseMetadataHandlerTest.java create mode 100644 athena-hbase/src/test/java/com/amazonaws/athena/connectors/hbase/HbaseRecordHandlerTest.java create mode 100644 athena-hbase/src/test/java/com/amazonaws/athena/connectors/hbase/HbaseSchemaUtilsTest.java create mode 100644 athena-hbase/src/test/java/com/amazonaws/athena/connectors/hbase/TestUtils.java create mode 100644 athena-jdbc/LICENSE.txt create mode 100644 athena-jdbc/README.md create mode 100644 athena-jdbc/athena-jdbc.yaml create mode 100644 athena-jdbc/pom.xml create mode 100644 athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/MultiplexingJdbcCompositeHandler.java create mode 100644 athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/MultiplexingJdbcMetadataHandler.java create mode 100644 athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/MultiplexingJdbcRecordHandler.java create mode 100644 athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/connection/DatabaseConnectionConfig.java create mode 100644 athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/connection/DatabaseConnectionConfigBuilder.java create mode 100644 athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/connection/DatabaseConnectionInfo.java create mode 100644 athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/connection/GenericJdbcConnectionFactory.java create mode 100644 athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/connection/JdbcConnectionFactory.java create mode 100644 athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/connection/JdbcCredential.java create mode 100644 athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/connection/JdbcCredentialProvider.java create mode 100644 athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/connection/RdsSecretsCredentialProvider.java create mode 100644 athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/connection/StaticJdbcCredentialProvider.java create mode 100644 athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/manager/JDBCUtil.java create mode 100644 athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/manager/JdbcArrowTypeConverter.java create mode 100644 athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/manager/JdbcMetadataHandler.java create mode 100644 athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/manager/JdbcRecordHandler.java create mode 100644 athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/manager/JdbcSplitQueryBuilder.java create mode 100644 athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/manager/PreparedStatementBuilder.java create mode 100644 athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/mysql/MySqlCompositeHandler.java create mode 100644 athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/mysql/MySqlMetadataHandler.java create mode 100644 athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/mysql/MySqlQueryStringBuilder.java create mode 100644 athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/mysql/MySqlRecordHandler.java create mode 100644 athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/postgresql/PostGreSqlCompositeHandler.java create mode 100644 athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/postgresql/PostGreSqlMetadataHandler.java create mode 100644 athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/postgresql/PostGreSqlQueryStringBuilder.java create mode 100644 athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/postgresql/PostGreSqlRecordHandler.java create mode 100644 athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/MultiplexingJdbcMetadataHandlerTest.java create mode 100644 athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/MultiplexingJdbcRecordHandlerTest.java create mode 100644 athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/TestBase.java create mode 100644 athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/connection/DatabaseConnectionConfigBuilderTest.java create mode 100644 athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/connection/JdbcCredentialProviderTest.java create mode 100644 athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/manager/JDBCUtilTest.java create mode 100644 athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/manager/JdbcArrowTypeConverterTest.java create mode 100644 athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/manager/JdbcMetadataHandlerTest.java create mode 100644 athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/manager/JdbcRecordHandlerTest.java create mode 100644 athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/mysql/MySqlMetadataHandlerTest.java create mode 100644 athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/mysql/MySqlRecordHandlerTest.java create mode 100644 athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/postgresql/PostGreSqlMetadataHandlerTest.java create mode 100644 athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/postgresql/PostGreSqlRecordHandlerTest.java create mode 100644 athena-redis/LICENSE.txt create mode 100644 athena-redis/README.md create mode 100644 athena-redis/athena-redis.yaml create mode 100644 athena-redis/pom.xml create mode 100644 athena-redis/src/main/java/com/amazonaws/athena/connectors/redis/JedisPoolFactory.java create mode 100644 athena-redis/src/main/java/com/amazonaws/athena/connectors/redis/KeyType.java create mode 100644 athena-redis/src/main/java/com/amazonaws/athena/connectors/redis/RedisCompositeHandler.java create mode 100644 athena-redis/src/main/java/com/amazonaws/athena/connectors/redis/RedisMetadataHandler.java create mode 100644 athena-redis/src/main/java/com/amazonaws/athena/connectors/redis/RedisRecordHandler.java create mode 100644 athena-redis/src/main/java/com/amazonaws/athena/connectors/redis/ValueConverter.java create mode 100644 athena-redis/src/main/java/com/amazonaws/athena/connectors/redis/ValueType.java create mode 100644 athena-redis/src/test/java/com/amazonaws/athena/connectors/redis/RedisMetadataHandlerTest.java create mode 100644 athena-redis/src/test/java/com/amazonaws/athena/connectors/redis/RedisRecordHandlerTest.java create mode 100644 athena-tpcds/LICENSE.txt create mode 100644 athena-tpcds/README.md create mode 100644 athena-tpcds/athena-tpcds.yaml create mode 100644 athena-tpcds/pom.xml create mode 100644 athena-tpcds/src/main/java/com/amazonaws/athena/connectors/tpcds/TPCDSCompositeHandler.java create mode 100644 athena-tpcds/src/main/java/com/amazonaws/athena/connectors/tpcds/TPCDSMetadataHandler.java create mode 100644 athena-tpcds/src/main/java/com/amazonaws/athena/connectors/tpcds/TPCDSRecordHandler.java create mode 100644 athena-tpcds/src/main/java/com/amazonaws/athena/connectors/tpcds/TPCDSUtils.java create mode 100644 athena-tpcds/src/main/resources/queries/q1.sql create mode 100644 athena-tpcds/src/main/resources/queries/q10.sql create mode 100644 athena-tpcds/src/main/resources/queries/q11.sql create mode 100644 athena-tpcds/src/main/resources/queries/q12.sql create mode 100644 athena-tpcds/src/main/resources/queries/q13.sql create mode 100644 athena-tpcds/src/main/resources/queries/q14a.sql create mode 100644 athena-tpcds/src/main/resources/queries/q14b.sql create mode 100644 athena-tpcds/src/main/resources/queries/q15.sql create mode 100644 athena-tpcds/src/main/resources/queries/q16.sql create mode 100644 athena-tpcds/src/main/resources/queries/q17.sql create mode 100644 athena-tpcds/src/main/resources/queries/q18.sql create mode 100644 athena-tpcds/src/main/resources/queries/q19.sql create mode 100644 athena-tpcds/src/main/resources/queries/q2.sql create mode 100644 athena-tpcds/src/main/resources/queries/q20.sql create mode 100644 athena-tpcds/src/main/resources/queries/q21.sql create mode 100644 athena-tpcds/src/main/resources/queries/q22.sql create mode 100644 athena-tpcds/src/main/resources/queries/q23a.sql create mode 100644 athena-tpcds/src/main/resources/queries/q23b.sql create mode 100644 athena-tpcds/src/main/resources/queries/q24a.sql create mode 100644 athena-tpcds/src/main/resources/queries/q24b.sql create mode 100644 athena-tpcds/src/main/resources/queries/q25.sql create mode 100644 athena-tpcds/src/main/resources/queries/q26.sql create mode 100644 athena-tpcds/src/main/resources/queries/q27.sql create mode 100644 athena-tpcds/src/main/resources/queries/q28.sql create mode 100644 athena-tpcds/src/main/resources/queries/q29.sql create mode 100644 athena-tpcds/src/main/resources/queries/q3.sql create mode 100644 athena-tpcds/src/main/resources/queries/q30.sql create mode 100644 athena-tpcds/src/main/resources/queries/q31.sql create mode 100644 athena-tpcds/src/main/resources/queries/q32.sql create mode 100644 athena-tpcds/src/main/resources/queries/q33.sql create mode 100644 athena-tpcds/src/main/resources/queries/q34.sql create mode 100644 athena-tpcds/src/main/resources/queries/q35.sql create mode 100644 athena-tpcds/src/main/resources/queries/q37.sql create mode 100644 athena-tpcds/src/main/resources/queries/q38.sql create mode 100644 athena-tpcds/src/main/resources/queries/q39a.sql create mode 100644 athena-tpcds/src/main/resources/queries/q39b.sql create mode 100644 athena-tpcds/src/main/resources/queries/q4.sql create mode 100644 athena-tpcds/src/main/resources/queries/q40.sql create mode 100644 athena-tpcds/src/main/resources/queries/q41.sql create mode 100644 athena-tpcds/src/main/resources/queries/q42.sql create mode 100644 athena-tpcds/src/main/resources/queries/q43.sql create mode 100644 athena-tpcds/src/main/resources/queries/q44.sql create mode 100644 athena-tpcds/src/main/resources/queries/q45.sql create mode 100644 athena-tpcds/src/main/resources/queries/q46.sql create mode 100644 athena-tpcds/src/main/resources/queries/q47.sql create mode 100644 athena-tpcds/src/main/resources/queries/q48.sql create mode 100644 athena-tpcds/src/main/resources/queries/q49.sql create mode 100644 athena-tpcds/src/main/resources/queries/q5.sql create mode 100644 athena-tpcds/src/main/resources/queries/q50.sql create mode 100644 athena-tpcds/src/main/resources/queries/q51.sql create mode 100644 athena-tpcds/src/main/resources/queries/q52.sql create mode 100644 athena-tpcds/src/main/resources/queries/q53.sql create mode 100644 athena-tpcds/src/main/resources/queries/q54.sql create mode 100644 athena-tpcds/src/main/resources/queries/q55.sql create mode 100644 athena-tpcds/src/main/resources/queries/q56.sql create mode 100644 athena-tpcds/src/main/resources/queries/q57.sql create mode 100644 athena-tpcds/src/main/resources/queries/q59.sql create mode 100644 athena-tpcds/src/main/resources/queries/q6.sql create mode 100644 athena-tpcds/src/main/resources/queries/q60.sql create mode 100644 athena-tpcds/src/main/resources/queries/q61.sql create mode 100644 athena-tpcds/src/main/resources/queries/q62.sql create mode 100644 athena-tpcds/src/main/resources/queries/q63.sql create mode 100644 athena-tpcds/src/main/resources/queries/q64.sql create mode 100644 athena-tpcds/src/main/resources/queries/q65.sql create mode 100644 athena-tpcds/src/main/resources/queries/q66.sql create mode 100644 athena-tpcds/src/main/resources/queries/q67.sql create mode 100644 athena-tpcds/src/main/resources/queries/q68.sql create mode 100644 athena-tpcds/src/main/resources/queries/q69.sql create mode 100644 athena-tpcds/src/main/resources/queries/q7.sql create mode 100644 athena-tpcds/src/main/resources/queries/q71.sql create mode 100644 athena-tpcds/src/main/resources/queries/q73.sql create mode 100644 athena-tpcds/src/main/resources/queries/q74.sql create mode 100644 athena-tpcds/src/main/resources/queries/q75.sql create mode 100644 athena-tpcds/src/main/resources/queries/q76.sql create mode 100644 athena-tpcds/src/main/resources/queries/q77.sql create mode 100644 athena-tpcds/src/main/resources/queries/q78.sql create mode 100644 athena-tpcds/src/main/resources/queries/q79.sql create mode 100644 athena-tpcds/src/main/resources/queries/q8.sql create mode 100644 athena-tpcds/src/main/resources/queries/q80.sql create mode 100644 athena-tpcds/src/main/resources/queries/q81.sql create mode 100644 athena-tpcds/src/main/resources/queries/q82.sql create mode 100644 athena-tpcds/src/main/resources/queries/q83.sql create mode 100644 athena-tpcds/src/main/resources/queries/q84.sql create mode 100644 athena-tpcds/src/main/resources/queries/q85.sql create mode 100644 athena-tpcds/src/main/resources/queries/q87.sql create mode 100644 athena-tpcds/src/main/resources/queries/q88.sql create mode 100644 athena-tpcds/src/main/resources/queries/q89.sql create mode 100644 athena-tpcds/src/main/resources/queries/q9.sql create mode 100644 athena-tpcds/src/main/resources/queries/q90.sql create mode 100644 athena-tpcds/src/main/resources/queries/q91.sql create mode 100644 athena-tpcds/src/main/resources/queries/q92.sql create mode 100644 athena-tpcds/src/main/resources/queries/q93.sql create mode 100644 athena-tpcds/src/main/resources/queries/q94.sql create mode 100644 athena-tpcds/src/main/resources/queries/q95.sql create mode 100644 athena-tpcds/src/main/resources/queries/q96.sql create mode 100644 athena-tpcds/src/main/resources/queries/q97.sql create mode 100644 athena-tpcds/src/main/resources/queries/q98.sql create mode 100644 athena-tpcds/src/main/resources/queries/q99.sql create mode 100644 athena-tpcds/src/test/java/com/amazonaws/athena/connectors/tpcds/TPCDSMetadataHandlerTest.java create mode 100644 athena-tpcds/src/test/java/com/amazonaws/athena/connectors/tpcds/TPCDSRecordHandlerTest.java create mode 100644 athena-udfs/LICENSE.txt create mode 100644 athena-udfs/README.md create mode 100644 athena-udfs/athena-udfs.yaml create mode 100644 athena-udfs/pom.xml create mode 100644 athena-udfs/src/main/java/com/amazonaws/athena/connectors/udfs/AthenaUDFHandler.java create mode 100644 athena-udfs/src/test/java/com/amazonaws/athena/connectors/udfs/AthenaUDFHandlerTest.java create mode 100644 checkstyle.xml create mode 100644 docs/img/athena_federation_demo.png create mode 100644 docs/img/athena_federation_flow.png create mode 100644 docs/img/athena_federation_summary.png create mode 100644 docs/img/hbase_glue_example.png create mode 100644 pom.xml create mode 100755 tools/prepare_dev_env.sh create mode 100755 tools/publish.sh create mode 100755 tools/validate_connector.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..90df607a3a --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*/target +*/dependency-reduced-pom.xml +.idea/ +/target/ +*/*.iml diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000..5b627cfa60 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,4 @@ +## Code of Conduct +This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). +For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact +opensource-codeofconduct@amazon.com with any additional questions or comments. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..914e0741d7 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,61 @@ +# Contributing Guidelines + +Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional +documentation, we greatly value feedback and contributions from our community. + +Please read through this document before submitting any issues or pull requests to ensure we have all the necessary +information to effectively respond to your bug report or contribution. + + +## Reporting Bugs/Feature Requests + +We welcome you to use the GitHub issue tracker to report bugs or suggest features. + +When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already +reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: + +* A reproducible test case or series of steps +* The version of our code being used +* Any modifications you've made relevant to the bug +* Anything unusual about your environment or deployment + + +## Contributing via Pull Requests +Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: + +1. You are working against the latest source on the *master* branch. +2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. +3. You open an issue to discuss any significant work - we would hate for your time to be wasted. + +To send us a pull request, please: + +1. Fork the repository. +2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. +3. Ensure local tests pass. +4. Commit to your fork using clear commit messages. +5. Send us a pull request, answering any default questions in the pull request interface. +6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. + +GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and +[creating a pull request](https://help.github.com/articles/creating-a-pull-request/). + + +## Finding contributions to work on +Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. + + +## Code of Conduct +This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). +For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact +opensource-codeofconduct@amazon.com with any additional questions or comments. + + +## Security issue notifications +If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. + + +## Licensing + +See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. + +We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..67db858821 --- /dev/null +++ b/LICENSE @@ -0,0 +1,175 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000000..b8d0d46a06 --- /dev/null +++ b/NOTICE @@ -0,0 +1 @@ +Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/README.md b/README.md new file mode 100644 index 0000000000..188b4b4f54 --- /dev/null +++ b/README.md @@ -0,0 +1,138 @@ +# Amazon Athena Query Federation + +The Amazon Athena Query Federation SDK allows you to customize Amazon Athena with your own code. This enables you to integrate with new data sources, proprietary data formats, or build in new user defined functions. Initially these customizations will be limited to the parts of a query that occur during a TableScan operation but will eventually be expanded to include other parts of the query lifecycle using the same easy to understand interface. + +This functionality is currently in **Public Preview** while customers provide us feedback on usability, ease of using the service or building new connectors. We do not recommend that you use these connectors in production or use this preview to make assumptions about the performance of Athena’s Federation features. As we receive more feedback, we will make improvements to the preview and lift raise limits associated with query/connector performance, APIs, SDKs, and user experience. The best way to understand the performance of Athena Data Source Connectors is to run a benchmark when they become generally available (GA) or review our performance guidance. + +![Architecture Image](https://github.com/awslabs/aws-athena-query-federation/blob/master/docs/img/athena_federation_summary.png?raw=true) + +We've written integrations with more than 20 databases, storage formats, and live APIs in order to refine this interface and balance flexibility with ease of use. We hope that making this SDK and initial set of connectors Open Source will allow us to continue to improve the experience and performance of Athena Query Federation. + +## Serverless Big Data Using AWS Lambda + +![Architecture Image](https://github.com/awslabs/aws-athena-query-federation/blob/master/docs/img/athena_federation_flow.png?raw=true) + +## Example Usages + +- SecretsManager integration +- Serverless Application Repository + +### Queries That Span Data Stores + +Imagine a hypothetical e-commerce company who's architecture uses: + +1. Payment processing in a secure VPC with transaction records stored in HBase on EMR +2. Redis is used to store active orders so that the processing engine can get fast access to them. +3. DocumentDB (e.g. a mongodb compatible store) for Customer account data like email address, shipping addresses, etc.. +4. Their e-commerce site using auto-scaling on Fargate with their product catalog in Amazon Aurora. +5. Cloudwatch Logs to house the Order Processor's log events. +6. A write-once-read-many datawarehouse on Redshift. +7. Shipment tracking data in DynamoDB. +8. A fleet of Drivers performing last-mile delivery while utilizing IoT enabled tablets. +9. Advertising conversion data from a 3rd part cloud provider. + +![Architecture Image](https://github.com/awslabs/aws-athena-query-federation/blob/master/docs/img/athena_federation_demo.png?raw=true) + +Customer service agents begin receiving calls about orders 'stuck' in a weird state. Some show as pending even though they have delivered, others show as delivered but haven't actually shipped. It would be great if we could quickly run a query across this diverse architecture to understand which orders might be affected and what they have in common. + +Using Amazon Athena Query Federation and many of the connectors found in this repository, our hypothetical e-commerce company would be able to run a query that: + +1. Grabs all active orders from Redis. (see athena-redis) +2. Joins against any orders with 'WARN' or 'ERROR' events in Cloudwatch logs by using regex matching and extraction. (see athena-cloudwatch) +3. Joins against our EC2 inventory to get the hostname(s) and status of the Order Processor(s) that logged the 'WARN' or 'ERROR'. (see athena-cmdb) +4. Joins against DocumentDB to obtain customer contact details for the affected orders. (see athena-docdb) +5. Joins against a scatter-gather query sent to the Driver Fleet via Android Push notification. (see athena-android) +6. Joins against DynamoDB to get shipping status and tracking details. (see athena-dynamodb) +8. Joins against HBase to get payment status for the affected orders. (see athena-hbase) +7. Joins against the advertising conversion data in BigQuery to see which promotions need to be applied if a re-order is needed. (see athena-bigquery) + +```sql +WITH logs + AS (SELECT log_stream, + message AS + order_processor_log, + Regexp_extract(message, '.*orderId=(\d+) .*', 1) AS orderId, + Regexp_extract(message, '(.*):.*', 1) AS log_level + FROM + "lambda:cloudwatch"."/var/ecommerce-engine/order-processor".all_log_streams + WHERE Regexp_extract(message, '(.*):.*', 1) != 'WARN'), + active_orders + AS (SELECT * + FROM redis.redis_db.redis_customer_orders), + order_processors + AS (SELECT instanceid, + publicipaddress, + state.NAME + FROM awscmdb.ec2.ec2_instances), + customer + AS (SELECT id, + email + FROM docdb.customers.customer_info), + addresses + AS (SELECT id, + is_residential, + address.street AS street + FROM docdb.customers.customer_addresses), + drivers + AS ( SELECT name as driver_name, + result_field as driver_order, + device_id as truck_id, + last_updated + FROM android.android.live_query where query_timeout = 5000 and query_min_results=5), + impressions + AS ( SELECT path as advertisement, + conversion + FROM bigquery.click_impressions.click_conversions), + shipments + AS ( SELECT order_id, + shipment_id, + from_unixtime(cast(shipped_date as double)) as shipment_time, + carrier + FROM lambda_ddb.default.order_shipments), + payments + AS ( SELECT "summary:order_id", + "summary:status", + "summary:cc_id", + "details:network" + FROM "hbase".hbase_payments.transactions) + +SELECT _key_ AS redis_order_id, + customer_id, + customer.email AS cust_email, + "summary:cc_id" AS credit_card, + "details:network" AS CC_type, + "summary:status" AS payment_status, + impressions.advertisement as advertisement, + status AS redis_status, + addresses.street AS street_address, + shipments.shipment_time as shipment_time, + shipments.carrier as shipment_carrier, + driver_name AS driver_name, + truck_id AS truck_id, + last_updated AS driver_updated, + publicipaddress AS ec2_order_processor, + NAME AS ec2_state, + log_level, + order_processor_log +FROM active_orders + LEFT JOIN logs + ON logs.orderid = active_orders._key_ + LEFT JOIN order_processors + ON logs.log_stream = order_processors.instanceid + LEFT JOIN customer + ON customer.id = customer_id + LEFT JOIN addresses + ON addresses.id = address_id + LEFT JOIN drivers + ON drivers.driver_order = active_orders._key_ + LEFT JOIN impressions + ON impressions.conversion = active_orders._key_ + LEFT JOIN shipments + ON shipments.order_id = active_orders._key_ + LEFT JOIN payments + ON payments."summary:order_id" = active_orders._key_ +``` + +## License + +This project is licensed under the Apache-2.0 License. diff --git a/athena-android/pom.xml b/athena-android/pom.xml new file mode 100644 index 0000000000..801e7933c9 --- /dev/null +++ b/athena-android/pom.xml @@ -0,0 +1,67 @@ + + + + aws-athena-query-federation + com.amazonaws + 1.0 + + 4.0.0 + + athena-android + + + + com.amazonaws + aws-athena-federation-sdk + ${aws-athena-federation-sdk.version} + + + com.google.firebase + firebase-admin + 6.10.0 + + + com.fasterxml.jackson.core + jackson-databind + 2.9.8 + + + com.amazonaws + aws-java-sdk-sqs + 1.11.636 + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.1 + + false + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + package + + shade + + + + + + + \ No newline at end of file diff --git a/athena-android/src/main/java/com/amazonaws/athena/connectors/android/AndroidDeviceTable.java b/athena-android/src/main/java/com/amazonaws/athena/connectors/android/AndroidDeviceTable.java new file mode 100644 index 0000000000..a64fd4fa52 --- /dev/null +++ b/athena-android/src/main/java/com/amazonaws/athena/connectors/android/AndroidDeviceTable.java @@ -0,0 +1,147 @@ +/*- + * #%L + * athena-android + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.android; + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import org.apache.arrow.vector.FieldVector; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Schema; + +public class AndroidDeviceTable +{ + private final TableName tableName; + private final Schema schema; + + public AndroidDeviceTable() + { + //Table name must match the firebase push subscription topic used on the devices + this.tableName = new TableName("android", "live_query"); + schema = new SchemaBuilder().newBuilder() + .addStringField("device_id") + .addStringField("name") + .addStringField("echo_value") + .addStringField("result_field") + .addField("last_updated", Types.MinorType.DATEMILLI.getType()) + .addIntField("score") + .addBigIntField("query_timeout") + .addBigIntField("query_min_results") + .addMetadata("device_id", "Android device id of the responding device.") + .addMetadata("name", "Name of the simulated device owner.") + .addMetadata("last_updated", "Last time this data was fetched") + .addMetadata("echo_value", "The value requested by the search.") + .addMetadata("result_field", "Flattened copy of the first value from the values field.") + .addMetadata("score", "Randomly generated score") + .addMetadata("query_timeout", "used to configure the number of milli-seconds the query waits for the min_results") + .addMetadata("query_min_results", "The min number of results to wait for.") + .build(); + } + + public TableName getTableName() + { + return tableName; + } + + public Schema getSchema() + { + return schema; + } + + public String getQueryMinResultsField() + { + return "query_min_results"; + } + + public String getQueryTimeout() + { + return "query_timeout"; + } + + public String getDeviceIdField() + { + return "device_id"; + } + + public String getLastUpdatedField() + { + return "last_updated"; + } + + public String getNameField() + { + return "name"; + } + + public String getEchoValueField() + { + return "echo_value"; + } + + public String getResultField() + { + return "result_field"; + } + + public String getScoreField() + { + return "score"; + } + + public FieldVector getQueryMinResultsField(Block block) + { + return block.getFieldVector("query_min_results"); + } + + public FieldVector getQueryTimeout(Block block) + { + return block.getFieldVector("query_timeout"); + } + + public FieldVector getDeviceIdField(Block block) + { + return block.getFieldVector("device_id"); + } + + public FieldVector getNameField(Block block) + { + return block.getFieldVector("name"); + } + + public FieldVector getLastUpdatedField(Block block) + { + return block.getFieldVector("last_updated"); + } + + public FieldVector getEchoValueField(Block block) + { + return block.getFieldVector("echo_value"); + } + + public FieldVector getResultField(Block block) + { + return block.getFieldVector("result_field"); + } + + public FieldVector getScoreField(Block block) + { + return block.getFieldVector("score"); + } +} diff --git a/athena-android/src/main/java/com/amazonaws/athena/connectors/android/AndroidMetadataHandler.java b/athena-android/src/main/java/com/amazonaws/athena/connectors/android/AndroidMetadataHandler.java new file mode 100644 index 0000000000..8f5d7a4da6 --- /dev/null +++ b/athena-android/src/main/java/com/amazonaws/athena/connectors/android/AndroidMetadataHandler.java @@ -0,0 +1,109 @@ +/*- + * #%L + * athena-android + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.android; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockWriter; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.spill.SpillLocation; +import com.amazonaws.athena.connector.lambda.handlers.MetadataHandler; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesResponse; +import com.amazonaws.athena.connector.lambda.security.EncryptionKey; +import com.amazonaws.athena.connector.lambda.security.EncryptionKeyFactory; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import org.apache.arrow.util.VisibleForTesting; + +import java.util.Collections; + +public class AndroidMetadataHandler + extends MetadataHandler +{ + private static final String sourceType = "android"; + private static final AndroidDeviceTable androidDeviceTable = new AndroidDeviceTable(); + + public AndroidMetadataHandler() + { + super(sourceType); + } + + @VisibleForTesting + protected AndroidMetadataHandler( + EncryptionKeyFactory keyFactory, + AWSSecretsManager secretsManager, + AmazonAthena athena, + String spillBucket, + String spillPrefix) + { + super(keyFactory, secretsManager, athena, sourceType, spillBucket, spillPrefix); + } + + @Override + public ListSchemasResponse doListSchemaNames(BlockAllocator blockAllocator, ListSchemasRequest listSchemasRequest) + { + String schemaName = androidDeviceTable.getTableName().getSchemaName(); + return new ListSchemasResponse(listSchemasRequest.getCatalogName(), Collections.singletonList(schemaName)); + } + + @Override + public ListTablesResponse doListTables(BlockAllocator blockAllocator, ListTablesRequest listTablesRequest) + { + return new ListTablesResponse(listTablesRequest.getCatalogName(), + Collections.singletonList(androidDeviceTable.getTableName())); + } + + @Override + public GetTableResponse doGetTable(BlockAllocator blockAllocator, GetTableRequest getTableRequest) + { + if (!androidDeviceTable.getTableName().equals(getTableRequest.getTableName())) { + throw new RuntimeException("Unknown table " + getTableRequest.getTableName()); + } + + return new GetTableResponse(getTableRequest.getCatalogName(), + androidDeviceTable.getTableName(), + androidDeviceTable.getSchema()); + } + + @Override + public void getPartitions(BlockWriter blockWriter, GetTableLayoutRequest request, QueryStatusChecker queryStatusChecker) + throws Exception + { + //NoOp since we don't support partitioning + } + + @Override + public GetSplitsResponse doGetSplits(BlockAllocator blockAllocator, GetSplitsRequest getSplitsRequest) + { + //Every split needs a unique spill location. + SpillLocation spillLocation = makeSpillLocation(getSplitsRequest); + EncryptionKey encryptionKey = makeEncryptionKey(); + Split split = Split.newBuilder(spillLocation, encryptionKey).build(); + return new GetSplitsResponse(getSplitsRequest.getCatalogName(), split); + } +} diff --git a/athena-android/src/main/java/com/amazonaws/athena/connectors/android/AndroidRecordHandler.java b/athena-android/src/main/java/com/amazonaws/athena/connectors/android/AndroidRecordHandler.java new file mode 100644 index 0000000000..1e27ec4dbd --- /dev/null +++ b/athena-android/src/main/java/com/amazonaws/athena/connectors/android/AndroidRecordHandler.java @@ -0,0 +1,174 @@ +/*- + * #%L + * athena-android + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.android; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockSpiller; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import com.amazonaws.athena.connector.lambda.handlers.RecordHandler; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.athena.AmazonAthenaClientBuilder; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.amazonaws.services.secretsmanager.AWSSecretsManagerClientBuilder; +import com.amazonaws.services.sqs.AmazonSQS; +import com.amazonaws.services.sqs.AmazonSQSClientBuilder; +import com.amazonaws.services.sqs.model.DeleteMessageBatchRequestEntry; +import com.amazonaws.services.sqs.model.GetQueueUrlResult; +import com.amazonaws.services.sqs.model.ReceiveMessageRequest; +import com.amazonaws.services.sqs.model.ReceiveMessageResult; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.arrow.util.VisibleForTesting; +import org.apache.arrow.vector.types.pojo.Field; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class AndroidRecordHandler + extends RecordHandler +{ + private static final String sourceType = "android"; + private static final Logger logger = LoggerFactory.getLogger(AndroidRecordHandler.class); + + private static final String FIREBASE_DB_URL = "FIREBASE_DB_URL"; + private static final String FIREBASE_CONFIG = "FIREBASE_CONFIG"; + private static final String RESPONSE_QUEUE_NAME = "RESPONSE_QUEUE_NAME"; + private static final String MAX_WAIT_TIME = "MAX_WAIT_TIME"; + private static final String MIN_RESULTS = "MIN_RESULTS"; + + private final AndroidDeviceTable androidTable = new AndroidDeviceTable(); + private final ObjectMapper mapper = new ObjectMapper(); + private final AmazonSQS amazonSQS; + private final LiveQueryService liveQueryService; + private final String queueUrl; + + public AndroidRecordHandler() + { + this(AmazonS3ClientBuilder.defaultClient(), + AWSSecretsManagerClientBuilder.defaultClient(), + AmazonAthenaClientBuilder.defaultClient(), + AmazonSQSClientBuilder.defaultClient(), + new LiveQueryService(System.getenv(FIREBASE_CONFIG), System.getenv(FIREBASE_DB_URL))); + } + + @VisibleForTesting + protected AndroidRecordHandler(AmazonS3 amazonS3, + AWSSecretsManager secretsManager, + AmazonAthena athena, + AmazonSQS amazonSQS, + LiveQueryService liveQueryService) + { + super(amazonS3, secretsManager, athena, sourceType); + this.amazonSQS = amazonSQS; + this.liveQueryService = liveQueryService; + GetQueueUrlResult queueUrlResult = amazonSQS.getQueueUrl(System.getenv(RESPONSE_QUEUE_NAME)); + queueUrl = queueUrlResult.getQueueUrl(); + } + + @Override + protected void readWithConstraint(BlockSpiller blockSpiller, ReadRecordsRequest readRecordsRequest, QueryStatusChecker queryStatusChecker) + { + QueryRequest request = QueryRequest.newBuilder() + .withQueryId(readRecordsRequest.getQueryId()) + .withQuery("query details") + .withResponseQueue(queueUrl) + .build(); + + String response = liveQueryService.broadcastQuery(readRecordsRequest.getTableName().getTableName(), request); + logger.info("readWithConstraint: Android broadcast result: " + response); + + readResultsFromSqs(blockSpiller, readRecordsRequest); + } + + private void readResultsFromSqs(BlockSpiller blockSpiller, ReadRecordsRequest readRecordsRequest) + { + final Map fields = new HashMap<>(); + readRecordsRequest.getSchema().getFields().forEach(next -> fields.put(next.getName(), next)); + + ReceiveMessageRequest receiveRequest = new ReceiveMessageRequest() + .withQueueUrl(queueUrl) + .withWaitTimeSeconds(1); + + ValueSet queryTimeoutValueSet = readRecordsRequest.getConstraints().getSummary().get(androidTable.getQueryTimeout()); + ValueSet minResultsValueSet = readRecordsRequest.getConstraints().getSummary().get(androidTable.getQueryMinResultsField()); + + long maxWaitTime = queryTimeoutValueSet != null && queryTimeoutValueSet.isSingleValue() ? + (long) queryTimeoutValueSet.getSingleValue() : Long.parseLong(System.getenv(MAX_WAIT_TIME)); + long minResults = minResultsValueSet != null && minResultsValueSet.isSingleValue() ? + (long) minResultsValueSet.getSingleValue() : Long.parseLong(System.getenv(MIN_RESULTS)); + + logger.info("readResultsFromSqs: using timeout of " + maxWaitTime + " ms and min_results of " + minResults); + + long startTime = System.currentTimeMillis(); + long numResults = 0; + ReceiveMessageResult receiveMessageResult; + List msgsToAck = new ArrayList<>(); + do { + receiveMessageResult = amazonSQS.receiveMessage(receiveRequest); + for (com.amazonaws.services.sqs.model.Message next : receiveMessageResult.getMessages()) { + try { + QueryResponse queryResponse = mapper.readValue(next.getBody(), QueryResponse.class); + if (queryResponse.getQueryId().equals(readRecordsRequest.getQueryId())) { + numResults++; + msgsToAck.add(new DeleteMessageBatchRequestEntry().withReceiptHandle(next.getReceiptHandle()).withId(next.getMessageId())); + blockSpiller.writeRows((Block block, int rowNum) -> { + int newRows = 0; + + for (String nextVal : queryResponse.getValues()) { + boolean matches = true; + int effectiveRow = newRows + rowNum; + + matches &= block.offerValue(androidTable.getDeviceIdField(), effectiveRow, queryResponse.getDeviceId()); + matches &= block.offerValue(androidTable.getNameField(), effectiveRow, queryResponse.getName()); + matches &= block.offerValue(androidTable.getEchoValueField(), effectiveRow, queryResponse.getEchoValue()); + matches &= block.offerValue(androidTable.getLastUpdatedField(), effectiveRow, System.currentTimeMillis()); + matches &= block.offerValue(androidTable.getResultField(), effectiveRow, nextVal); + matches &= block.offerValue(androidTable.getScoreField(), effectiveRow, queryResponse.getRandom()); + matches &= block.offerValue(androidTable.getQueryMinResultsField(), effectiveRow, minResults); + matches &= block.offerValue(androidTable.getQueryTimeout(), effectiveRow, maxWaitTime); + + newRows += matches ? 1 : 0; + } + + return newRows; + }); + logger.info("Received matching response " + queryResponse.toString()); + } + } + catch (RuntimeException | IOException ex) { + logger.error("Error processing msg", ex); + } + } + if (!msgsToAck.isEmpty()) { + amazonSQS.deleteMessageBatch(queueUrl, msgsToAck); + msgsToAck.clear(); + } + } + while (System.currentTimeMillis() - startTime < maxWaitTime && (numResults < minResults || receiveMessageResult.getMessages().size() > 0)); + } +} diff --git a/athena-android/src/main/java/com/amazonaws/athena/connectors/android/LiveQueryService.java b/athena-android/src/main/java/com/amazonaws/athena/connectors/android/LiveQueryService.java new file mode 100644 index 0000000000..2e287c2092 --- /dev/null +++ b/athena-android/src/main/java/com/amazonaws/athena/connectors/android/LiveQueryService.java @@ -0,0 +1,68 @@ +/*- + * #%L + * athena-android + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.android; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.auth.oauth2.GoogleCredentials; +import com.google.firebase.FirebaseApp; +import com.google.firebase.FirebaseOptions; +import com.google.firebase.messaging.FirebaseMessaging; +import com.google.firebase.messaging.FirebaseMessagingException; +import com.google.firebase.messaging.Message; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public class LiveQueryService +{ + private static final String PUSH_MSG_FIELD = "query_request"; + private final ObjectMapper mapper = new ObjectMapper(); + + public LiveQueryService(String authConfig, String databaseUrl) + { + try { + InputStream inputStream = new ByteArrayInputStream(authConfig.getBytes()); + FirebaseOptions options = new FirebaseOptions.Builder() + .setCredentials(GoogleCredentials.fromStream(inputStream)) + .setDatabaseUrl(databaseUrl) + .build(); + + FirebaseApp.initializeApp(options); + } + catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + public String broadcastQuery(String topic, QueryRequest query) + { + try { + Message.Builder messageBuilder = Message.builder(); + messageBuilder.putData(PUSH_MSG_FIELD, mapper.writeValueAsString(query)); + messageBuilder.setTopic(topic); + return FirebaseMessaging.getInstance().send(messageBuilder.build()); + } + catch (JsonProcessingException | FirebaseMessagingException ex) { + throw new RuntimeException(ex); + } + } +} diff --git a/athena-android/src/main/java/com/amazonaws/athena/connectors/android/QueryRequest.java b/athena-android/src/main/java/com/amazonaws/athena/connectors/android/QueryRequest.java new file mode 100644 index 0000000000..01b8e8ae3f --- /dev/null +++ b/athena-android/src/main/java/com/amazonaws/athena/connectors/android/QueryRequest.java @@ -0,0 +1,132 @@ +/*- + * #%L + * athena-android + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.android; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class QueryRequest +{ + private final String queryId; + private final String query; + private final String echoValue; + private final String responseQueue; + + @JsonCreator + public QueryRequest(@JsonProperty("queryId") String queryId, + @JsonProperty("query") String query, + @JsonProperty("echoValue") String echoValue, + @JsonProperty("responseQueue") String responseQueue) + { + this.queryId = queryId; + this.query = query; + this.echoValue = echoValue; + this.responseQueue = responseQueue; + } + + private QueryRequest(Builder builder) + { + queryId = builder.queryId; + query = builder.query; + echoValue = builder.echoValue; + responseQueue = builder.responseQueue; + } + + public static Builder newBuilder() + { + return new Builder(); + } + + @JsonProperty("query") + public String getQuery() + { + return query; + } + + @JsonProperty("echoValue") + public String getEchoValue() + { + return echoValue; + } + + @JsonProperty("queryId") + public String getQueryId() + { + return queryId; + } + + @JsonProperty("responseQueue") + public String getResponseQueue() + { + return responseQueue; + } + + @Override + public String toString() + { + return "QueryRequest{" + + "queryId='" + queryId + '\'' + + ", query='" + query + '\'' + + ", echoValue='" + echoValue + '\'' + + ", responseQueue='" + responseQueue + '\'' + + '}'; + } + + public static final class Builder + { + private String queryId; + private String query; + private String echoValue; + private String responseQueue; + + private Builder() + { + } + + public Builder withQuery(String val) + { + query = val; + return this; + } + + public Builder withEchoValue(String val) + { + echoValue = val; + return this; + } + + public Builder withResponseQueue(String val) + { + responseQueue = val; + return this; + } + + public Builder withQueryId(String val) + { + queryId = val; + return this; + } + + public QueryRequest build() + { + return new QueryRequest(this); + } + } +} diff --git a/athena-android/src/main/java/com/amazonaws/athena/connectors/android/QueryResponse.java b/athena-android/src/main/java/com/amazonaws/athena/connectors/android/QueryResponse.java new file mode 100644 index 0000000000..b556d024e2 --- /dev/null +++ b/athena-android/src/main/java/com/amazonaws/athena/connectors/android/QueryResponse.java @@ -0,0 +1,170 @@ +/*- + * #%L + * athena-android + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.android; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +public class QueryResponse +{ + private final String deviceId; + private final String queryId; + private final String name; + private final String echoValue; + private final List values; + private final int random; + + @JsonCreator + public QueryResponse(@JsonProperty("deviceId") String deviceId, + @JsonProperty("queryId") String queryId, + @JsonProperty("name") String name, + @JsonProperty("echoValue") String echoValue, + @JsonProperty("values") List values, + @JsonProperty("random") int random) + { + this.deviceId = deviceId; + this.queryId = queryId; + this.name = name; + this.echoValue = echoValue; + this.values = values; + this.random = random; + } + + private QueryResponse(Builder builder) + { + queryId = builder.queryId; + deviceId = builder.deviceId; + name = builder.name; + echoValue = builder.echoValue; + values = builder.values; + random = builder.random; + } + + public static Builder newBuilder() + { + return new Builder(); + } + + @JsonProperty("deviceId") + public String getDeviceId() + { + return deviceId; + } + + @JsonProperty("queryId") + public String getQueryId() + { + return queryId; + } + + @JsonProperty("name") + public String getName() + { + return name; + } + + @JsonProperty("echoValue") + public String getEchoValue() + { + return echoValue; + } + + @JsonProperty("values") + public List getValues() + { + return values; + } + + @JsonProperty("random") + public int getRandom() + { + return random; + } + + @Override + public String toString() + { + return "QueryResponse{" + + "deviceId='" + deviceId + '\'' + + ", queryId='" + queryId + '\'' + + ", name='" + name + '\'' + + ", echoValue='" + echoValue + '\'' + + ", values=" + values + + ", random=" + random + + '}'; + } + + public static final class Builder + { + private String deviceId; + private String queryId; + private String name; + private String echoValue; + private List values; + private int random; + + private Builder() + { + } + + public Builder withDeviceId(String val) + { + deviceId = val; + return this; + } + + public Builder withQueryId(String val) + { + queryId = val; + return this; + } + + public Builder withEchoValue(String val) + { + echoValue = val; + return this; + } + + public Builder withName(String val) + { + name = val; + return this; + } + + public Builder withValues(List val) + { + values = val; + return this; + } + + public Builder withRandom(int val) + { + random = val; + return this; + } + + public QueryResponse build() + { + return new QueryResponse(this); + } + } +} diff --git a/athena-aws-cmdb/LICENSE.txt b/athena-aws-cmdb/LICENSE.txt new file mode 100644 index 0000000000..418de4c108 --- /dev/null +++ b/athena-aws-cmdb/LICENSE.txt @@ -0,0 +1,174 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. \ No newline at end of file diff --git a/athena-aws-cmdb/README.md b/athena-aws-cmdb/README.md new file mode 100644 index 0000000000..1cfdfa2925 --- /dev/null +++ b/athena-aws-cmdb/README.md @@ -0,0 +1,66 @@ +# Amazon Athena AWS CMDB Connector + +This connector enables Amazon Athena to communicate with various AWS Services, making your AWS Resource inventory accessible via SQL. + +## Usage + +### Parameters + +The Athena AWS CMDB Connector provides several configuration options via Lambda environment variables. More detail on the available parameters can be found below. + +1. **spill_bucket** - When the data returned by your Lambda function exceeds Lambda’s limits, this is the bucket that the data will be written to for Athena to read the excess from. (e.g. my_bucket) +2. **spill_prefix** - (Optional) Defaults to sub-folder in your bucket called 'athena-federation-spill'. Used in conjunction with spill_bucket, this is the path within the above bucket that large responses are spilled to. You should configure an S3 lifecycle on this location to delete old spills after X days/Hours. +3. **kms_key_id** - (Optional) By default any data that is spilled to S3 is encrypted using AES-GCM and a randomly generated key. Setting a KMS Key ID allows your Lambda function to use KMS for key generation for a stronger source of encryption keys. (e.g. a7e63k4b-8loc-40db-a2a1-4d0en2cd8331) +4. **disable_spill_encryption** - (Optional) Defaults to False so that any data that is spilled to S3 is encrypted using AES-GMC either with a randomly generated key or using KMS to generate keys. Setting this to false will disable spill encryption. You may wish to disable this for improved performance, especially if your spill location in S3 uses S3 Server Side Encryption. (e.g. True or False) +5. **default_ec2_image_owner** - (Optional) When set, this controls the default ec2 image (aka AMI) owner used to filter AMIs. When this isn't set and your query against the ec2 images table does not include a filter for owner you will get a large number of results since the response will include all public images. + +### Databases & Tables + +The Athena AWS CMDB Connector makes the following databases and tables available for querying your AWS Resource Inventory. For more information on the columns available in each table, try running a 'describe database.table' from the Athena Console or API. + +1. **ec2** - This database contains EC2 related resources, including: + * **ebs_volumes** - Contains details of you EBS volumes. + * **ec2_instances** - Contains details of your EC2 Instances. + * **ec2_images** - Contains details of your EC2 Instance images. + * **routing_tables** - Contains details of your VPC Routing Tables. + * **security_groups** - Contains details of your Security Groups. + * **subnets** - Contains details of your VPC Subnets. + * **vpcs** - Contains details of your VPCs. +2. **emr** - This database contains EMR related resources, including: + * **emr_clusters** - Contains details of your EMR Clusters. +3. **rds** - This database contains RDS related resources, including: + * **rds_instances** - Contains details of your RDS Instances. +4. **s3** - This database contains RDS related resources, including: + * **buckets** - Contains details of your S3 buckets. + * **objects** - Contains details of your S3 Objects (excludes their contents). + +### Required Permissions + +Review the "Policies" section of the athena-aws-cmdb.yaml file for full details on the IAM Policies required by this connector. A brief summary is below. + +1. S3 Write Access - In order to successfully handle large queries, the connector requires write access to a location in S3. +1. EC2 Describe - The connector uses this access to describe your EC2 Instances, Security Groups, VPCs, EBS Volumes, etc... +1. EMR Describe / List - The connector uses this access to describe your EMR Clusters. +1. RDS Describe - The connector uses this access to describe your RDS Instances. +1. S3 List - The connector uses this access to list your buckets and objects. +1. Athena GetQueryExecution - The connector uses this access to fast-fail when the upstream Athena query has terminated. + +### Deploying The Connector + +To use this connector in your queries, navigate to AWS Serverless Application Repository and deploy a pre-built version of this connector. Alternatively, you can build and deploy this connector from source follow the below steps or use the more detailed tutorial in the athena-example module: + +1. From the athena-federation-sdk dir, run `mvn clean install` if you haven't already. +2. From the athena-aws-cmdb dir, run `mvn clean install`. +3. From the athena-aws-cmdb dir, run `../tools/publish.sh S3_BUCKET_NAME athena-aws-cmdb` to publish the connector to your private AWS Serverless Application Repository. The S3_BUCKET in the command is where a copy of the connector's code will be stored for Serverless Application Repository to retrieve it. This will allow users with permission to do so, the ability to deploy instances of the connector via 1-Click form. Then navigate to [Serverless Application Repository](https://aws.amazon.com/serverless/serverlessrepo) +4. Try running a query like the one below in Athena: +```sql +select * from "lambda:".ec2.ec2_instances limit 100 +``` + +## Performance + +The Athena AWS CMDB Connector does not current support parallel scans. Predicate Pushdown is performed within the Lambda function and where possible partial predicates are pushed to the services being queried. For example, a query for the details of a specific EC2 Instance will turn into a targeted describe of that specific instance id against the EC2 API. + +## License + +This project is licensed under the Apache-2.0 License. \ No newline at end of file diff --git a/athena-aws-cmdb/athena-aws-cmdb.yaml b/athena-aws-cmdb/athena-aws-cmdb.yaml new file mode 100644 index 0000000000..76225a36b3 --- /dev/null +++ b/athena-aws-cmdb/athena-aws-cmdb.yaml @@ -0,0 +1,73 @@ +Transform: 'AWS::Serverless-2016-10-31' +Metadata: + 'AWS::ServerlessRepo::Application': + Name: AthenaAwsCmdbConnector + Description: 'This connector enables Amazon Athena to communicate with various AWS Services, making your resource inventories accessible via SQL.' + Author: 'Amazon Athena' + SpdxLicenseId: Apache-2.0 + LicenseUrl: LICENSE.txt + ReadmeUrl: README.md + Labels: + - athena-federation + HomePageUrl: 'https://github.com/awslabs/aws-athena-query-federation' + SemanticVersion: 1.0.0 + SourceCodeUrl: 'https://github.com/awslabs/aws-athena-query-federation' +Parameters: + AthenaCatalogName: + Description: 'The name you will give to this catalog in Athena. It will also be used as the function name.' + Type: String + SpillBucket: + Description: 'The bucket where this function can spill data.' + Type: String + SpillPrefix: + Description: 'The bucket prefix where this function can spill large responses.' + Type: String + Default: athena-spill + LambdaTimeout: + Description: 'Maximum Lambda invocation runtime in seconds. (min 1 - 900 max)' + Default: 900 + Type: Number + LambdaMemory: + Description: 'Lambda memory in MB (min 128 - 3008 max).' + Default: 3008 + Type: Number + DisableSpillEncryption: + Description: "WARNING: If set to 'true' encryption for spilled data is disabled." + Default: 'false' + Type: String +Resources: + ConnectorConfig: + Type: 'AWS::Serverless::Function' + Properties: + Environment: + Variables: + disable_spill_encryption: !Ref DisableSpillEncryption + spill_bucket: !Ref SpillBucket + spill_prefix: !Ref SpillPrefix + FunctionName: !Ref AthenaCatalogName + Handler: "com.amazonaws.athena.connectors.aws.cmdb.AwsCmdbCompositeHandler" + CodeUri: "./target/athena-aws-cmdb-1.0.jar" + Description: "Enables Amazon Athena to communicate with various AWS Services, making your resource inventories accessible via SQL." + Runtime: java8 + Timeout: !Ref LambdaTimeout + MemorySize: !Ref LambdaMemory + Policies: + - Statement: + - Action: + - autoscaling:Describe* + - elasticloadbalancing:Describe* + - ec2:Describe* + - elasticmapreduce:Describe* + - elasticmapreduce:List* + - rds:Describe* + - rds:ListTagsForResource + - athena:GetQueryExecution + - s3:ListAllMyBuckets + - s3:ListBucket + Effect: Allow + Resource: '*' + Version: '2012-10-17' + #S3CrudPolicy allows our connector to spill large responses to S3. You can optionally replace this pre-made policy + #with one that is more restrictive and can only 'put' but not read,delete, or overwrite files. + - S3CrudPolicy: + BucketName: !Ref SpillBucket \ No newline at end of file diff --git a/athena-aws-cmdb/pom.xml b/athena-aws-cmdb/pom.xml new file mode 100644 index 0000000000..981a2aac8b --- /dev/null +++ b/athena-aws-cmdb/pom.xml @@ -0,0 +1,66 @@ + + + + aws-athena-query-federation + com.amazonaws + 1.0 + + 4.0.0 + + athena-aws-cmdb + + + com.amazonaws + aws-athena-federation-sdk + ${aws-athena-federation-sdk.version} + + + com.amazonaws + aws-java-sdk-ec2 + ${aws-sdk.version} + + + com.amazonaws + aws-java-sdk-emr + ${aws-sdk.version} + + + com.amazonaws + aws-java-sdk-rds + ${aws-sdk.version} + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.1 + + false + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + package + + shade + + + + + + + \ No newline at end of file diff --git a/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/AwsCmdbCompositeHandler.java b/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/AwsCmdbCompositeHandler.java new file mode 100644 index 0000000000..8036fd1e31 --- /dev/null +++ b/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/AwsCmdbCompositeHandler.java @@ -0,0 +1,35 @@ +/*- + * #%L + * athena-aws-cmdb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.aws.cmdb; + +import com.amazonaws.athena.connector.lambda.handlers.CompositeHandler; + +/** + * Boilerplate composite handler that allows us to use a single Lambda function for both + * Metadata and Data. In this case we just compose AwsCmdbMetadataHandler and AwsCmdbRecordHandler. + */ +public class AwsCmdbCompositeHandler + extends CompositeHandler +{ + public AwsCmdbCompositeHandler() + { + super(new AwsCmdbMetadataHandler(), new AwsCmdbRecordHandler()); + } +} diff --git a/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/AwsCmdbMetadataHandler.java b/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/AwsCmdbMetadataHandler.java new file mode 100644 index 0000000000..78cd23ddc2 --- /dev/null +++ b/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/AwsCmdbMetadataHandler.java @@ -0,0 +1,176 @@ +/*- + * #%L + * athena-aws-cmdb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.aws.cmdb; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockWriter; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.spill.SpillLocation; +import com.amazonaws.athena.connector.lambda.handlers.MetadataHandler; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesResponse; +import com.amazonaws.athena.connector.lambda.security.EncryptionKey; +import com.amazonaws.athena.connector.lambda.security.EncryptionKeyFactory; +import com.amazonaws.athena.connectors.aws.cmdb.tables.TableProvider; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import org.apache.arrow.util.VisibleForTesting; + +import java.util.List; +import java.util.Map; + +/** + * Handles metadata requests for the Athena AWS CMDB Connector. + *

+ * For more detail, please see the module's README.md, some notable characteristics of this class include: + *

+ * 1. Maps AWS Resources to SQL tables using a set of TableProviders constructed from a TableProviderFactory. + * 2. This class is largely a mux that delegates requests to the appropriate TableProvider based on the + * requested TableName. + * 3. Provides a schema and table list by scanning all loaded TableProviders. + */ +public class AwsCmdbMetadataHandler + extends MetadataHandler +{ + private static final String SOURCE_TYPE = "cmdb"; + //Map of schema name to list of TableNames generated by scanning all loaded TableProviders. + private Map> schemas; + //Map of available fully qualified TableNames to their respective TableProviders. + private Map tableProviders; + + public AwsCmdbMetadataHandler() + { + super(SOURCE_TYPE); + TableProviderFactory tableProviderFactory = new TableProviderFactory(); + schemas = tableProviderFactory.getSchemas(); + tableProviders = tableProviderFactory.getTableProviders(); + } + + @VisibleForTesting + protected AwsCmdbMetadataHandler(TableProviderFactory tableProviderFactory, + EncryptionKeyFactory keyFactory, + AWSSecretsManager secretsManager, + AmazonAthena athena, + String spillBucket, + String spillPrefix) + { + super(keyFactory, secretsManager, athena, SOURCE_TYPE, spillBucket, spillPrefix); + schemas = tableProviderFactory.getSchemas(); + tableProviders = tableProviderFactory.getTableProviders(); + } + + /** + * Returns the list of supported schemas discovered from the loaded TableProvider scan. + * + * @see MetadataHandler + */ + @Override + public ListSchemasResponse doListSchemaNames(BlockAllocator blockAllocator, ListSchemasRequest listSchemasRequest) + { + return new ListSchemasResponse(listSchemasRequest.getCatalogName(), schemas.keySet()); + } + + /** + * Returns the list of supported tables on the requested schema discovered from the loaded TableProvider scan. + * + * @see MetadataHandler + */ + @Override + public ListTablesResponse doListTables(BlockAllocator blockAllocator, ListTablesRequest listTablesRequest) + { + return new ListTablesResponse(listTablesRequest.getCatalogName(), schemas.get(listTablesRequest.getSchemaName())); + } + + /** + * Delegates to the TableProvider that is registered for the requested table. + * + * @see MetadataHandler + */ + @Override + public GetTableResponse doGetTable(BlockAllocator blockAllocator, GetTableRequest getTableRequest) + { + TableProvider tableProvider = tableProviders.get(getTableRequest.getTableName()); + if (tableProvider == null) { + throw new RuntimeException("Unknown table " + getTableRequest.getTableName()); + } + return tableProvider.getTable(blockAllocator, getTableRequest); + } + + /** + * Delegates to the TableProvider that is registered for the requested table. + * + * @see MetadataHandler + */ + @Override + public void enhancePartitionSchema(SchemaBuilder partitionSchemaBuilder, GetTableLayoutRequest request) + { + TableProvider tableProvider = tableProviders.get(request.getTableName()); + if (tableProvider == null) { + throw new RuntimeException("Unknown table " + request.getTableName()); + } + tableProvider.enhancePartitionSchema(partitionSchemaBuilder, request); + } + + /** + * Delegates to the TableProvider that is registered for the requested table. + * + * @see MetadataHandler + */ + @Override + public void getPartitions(BlockWriter blockWriter, GetTableLayoutRequest request, QueryStatusChecker queryStatusChecker) + throws Exception + { + TableProvider tableProvider = tableProviders.get(request.getTableName()); + if (tableProvider == null) { + throw new RuntimeException("Unknown table " + request.getTableName()); + } + tableProvider.getPartitions(blockWriter, request); + } + + /** + * Delegates to the TableProvider that is registered for the requested table. + * + * @see MetadataHandler + */ + @Override + public GetSplitsResponse doGetSplits(BlockAllocator blockAllocator, GetSplitsRequest getSplitsRequest) + { + TableProvider tableProvider = tableProviders.get(getSplitsRequest.getTableName()); + if (tableProvider == null) { + throw new RuntimeException("Unknown table " + getSplitsRequest.getTableName()); + } + + //Every split needs a unique spill location. + SpillLocation spillLocation = makeSpillLocation(getSplitsRequest); + EncryptionKey encryptionKey = makeEncryptionKey(); + Split split = Split.newBuilder(spillLocation, encryptionKey).build(); + return new GetSplitsResponse(getSplitsRequest.getCatalogName(), split); + } +} diff --git a/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/AwsCmdbRecordHandler.java b/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/AwsCmdbRecordHandler.java new file mode 100644 index 0000000000..ea78f6d996 --- /dev/null +++ b/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/AwsCmdbRecordHandler.java @@ -0,0 +1,76 @@ +/*- + * #%L + * athena-aws-cmdb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.aws.cmdb; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.BlockSpiller; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.handlers.RecordHandler; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.athena.connectors.aws.cmdb.tables.TableProvider; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import org.apache.arrow.util.VisibleForTesting; + +import java.util.Map; + +/** + * Handles record requests for the Athena AWS CMDB Connector. + *

+ * For more detail, please see the module's README.md, some notable characteristics of this class include: + *

+ * 1. Maps AWS Resources to SQL tables using a set of TableProviders constructed from a TableProviderFactory. + * 2. This class is largely a mux that delegates requests to the appropriate TableProvider based on the + * requested TableName. + */ +public class AwsCmdbRecordHandler + extends RecordHandler +{ + private static final String SOURCE_TYPE = "cmdb"; + + //Map of available fully qualified TableNames to their respective TableProviders. + private Map tableProviders; + + public AwsCmdbRecordHandler() + { + super(SOURCE_TYPE); + tableProviders = new TableProviderFactory().getTableProviders(); + } + + @VisibleForTesting + protected AwsCmdbRecordHandler(AmazonS3 amazonS3, AWSSecretsManager secretsManager, AmazonAthena athena, TableProviderFactory tableProviderFactory) + { + super(amazonS3, secretsManager, athena, SOURCE_TYPE); + tableProviders = tableProviderFactory.getTableProviders(); + } + + /** + * Delegates to the TableProvider that is registered for the requested table. + * + * @see RecordHandler + */ + @Override + protected void readWithConstraint(BlockSpiller blockSpiller, ReadRecordsRequest readRecordsRequest, QueryStatusChecker queryStatusChecker) + { + TableProvider tableProvider = tableProviders.get(readRecordsRequest.getTableName()); + tableProvider.readWithConstraint(blockSpiller, readRecordsRequest, queryStatusChecker); + } +} diff --git a/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/TableProviderFactory.java b/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/TableProviderFactory.java new file mode 100644 index 0000000000..11259c0c2a --- /dev/null +++ b/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/TableProviderFactory.java @@ -0,0 +1,123 @@ +/*- + * #%L + * athena-aws-cmdb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.aws.cmdb; + +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connectors.aws.cmdb.tables.EmrClusterTableProvider; +import com.amazonaws.athena.connectors.aws.cmdb.tables.RdsTableProvider; +import com.amazonaws.athena.connectors.aws.cmdb.tables.TableProvider; +import com.amazonaws.athena.connectors.aws.cmdb.tables.ec2.EbsTableProvider; +import com.amazonaws.athena.connectors.aws.cmdb.tables.ec2.Ec2TableProvider; +import com.amazonaws.athena.connectors.aws.cmdb.tables.ec2.ImagesTableProvider; +import com.amazonaws.athena.connectors.aws.cmdb.tables.ec2.RouteTableProvider; +import com.amazonaws.athena.connectors.aws.cmdb.tables.ec2.SecurityGroupsTableProvider; +import com.amazonaws.athena.connectors.aws.cmdb.tables.ec2.SubnetTableProvider; +import com.amazonaws.athena.connectors.aws.cmdb.tables.ec2.VpcTableProvider; +import com.amazonaws.athena.connectors.aws.cmdb.tables.s3.S3BucketsTableProvider; +import com.amazonaws.athena.connectors.aws.cmdb.tables.s3.S3ObjectsTableProvider; +import com.amazonaws.services.ec2.AmazonEC2; +import com.amazonaws.services.ec2.AmazonEC2ClientBuilder; +import com.amazonaws.services.elasticmapreduce.AmazonElasticMapReduce; +import com.amazonaws.services.elasticmapreduce.AmazonElasticMapReduceClientBuilder; +import com.amazonaws.services.rds.AmazonRDS; +import com.amazonaws.services.rds.AmazonRDSClientBuilder; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import org.apache.arrow.util.VisibleForTesting; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Acts as a factory for all supported TableProviders and also a source of meta-data about the + * schemas and tables that the loaded TableProviders support. + */ +public class TableProviderFactory +{ + private Map> schemas = new HashMap<>(); + private Map tableProviders = new HashMap<>(); + + public TableProviderFactory() + { + this(AmazonEC2ClientBuilder.standard().build(), + AmazonElasticMapReduceClientBuilder.standard().build(), + AmazonRDSClientBuilder.standard().build(), + AmazonS3ClientBuilder.standard().build()); + } + + @VisibleForTesting + protected TableProviderFactory(AmazonEC2 ec2, AmazonElasticMapReduce emr, AmazonRDS rds, AmazonS3 amazonS3) + { + addProvider(new Ec2TableProvider(ec2)); + addProvider(new EbsTableProvider(ec2)); + addProvider(new VpcTableProvider(ec2)); + addProvider(new SecurityGroupsTableProvider(ec2)); + addProvider(new RouteTableProvider(ec2)); + addProvider(new SubnetTableProvider(ec2)); + addProvider(new ImagesTableProvider(ec2)); + addProvider(new EmrClusterTableProvider(emr)); + addProvider(new RdsTableProvider(rds)); + addProvider(new S3ObjectsTableProvider(amazonS3)); + addProvider(new S3BucketsTableProvider(amazonS3)); + } + + /** + * Adds a new TableProvider to the loaded set, if and only if, no existing TableProvider is known + * for the fully qualified table represented by the new TableProvider we are attempting to add. + * + * @param provider The TableProvider to add. + */ + private void addProvider(TableProvider provider) + { + if (tableProviders.putIfAbsent(provider.getTableName(), provider) != null) { + throw new RuntimeException("Duplicate provider for " + provider.getTableName()); + } + + List tables = schemas.get(provider.getSchema()); + if (tables == null) { + tables = new ArrayList<>(); + schemas.put(provider.getSchema(), tables); + } + tables.add(provider.getTableName()); + } + + /** + * Provides access to the mapping of loaded TableProviders by their fully qualified table names. + * + * @return Map of TableNames to their corresponding TableProvider. + */ + public Map getTableProviders() + { + return tableProviders; + } + + /** + * Provides access to the mapping of TableNames for each schema name discovered during the TableProvider + * scann. + * + * @return Map of schema names to their corresponding list of fully qualified TableNames. + */ + public Map> getSchemas() + { + return schemas; + } +} diff --git a/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/EmrClusterTableProvider.java b/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/EmrClusterTableProvider.java new file mode 100644 index 0000000000..ee3b15da91 --- /dev/null +++ b/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/EmrClusterTableProvider.java @@ -0,0 +1,205 @@ +/*- + * #%L + * athena-aws-cmdb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.aws.cmdb.tables; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockSpiller; +import com.amazonaws.athena.connector.lambda.data.FieldResolver; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.services.elasticmapreduce.AmazonElasticMapReduce; +import com.amazonaws.services.elasticmapreduce.model.Cluster; +import com.amazonaws.services.elasticmapreduce.model.ClusterSummary; +import com.amazonaws.services.elasticmapreduce.model.DescribeClusterRequest; +import com.amazonaws.services.elasticmapreduce.model.DescribeClusterResult; +import com.amazonaws.services.elasticmapreduce.model.ListClustersRequest; +import com.amazonaws.services.elasticmapreduce.model.ListClustersResult; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Schema; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * Maps your EMR Clusters to a table. + */ +public class EmrClusterTableProvider + implements TableProvider +{ + private static final Schema SCHEMA; + private AmazonElasticMapReduce emr; + + public EmrClusterTableProvider(AmazonElasticMapReduce emr) + { + this.emr = emr; + } + + /** + * @See TableProvider + */ + @Override + public String getSchema() + { + return "emr"; + } + + /** + * @See TableProvider + */ + @Override + public TableName getTableName() + { + return new TableName(getSchema(), "emr_clusters"); + } + + /** + * @See TableProvider + */ + @Override + public GetTableResponse getTable(BlockAllocator blockAllocator, GetTableRequest getTableRequest) + { + return new GetTableResponse(getTableRequest.getCatalogName(), getTableName(), SCHEMA); + } + + /** + * Calls ListClusters and DescribeCluster on the AWS EMR Client returning all clusters that match the supplied + * predicate and attempting to push down certain predicates (namely queries for specific cluster) to EC2. + * + * @See TableProvider + */ + @Override + public void readWithConstraint(BlockSpiller spiller, ReadRecordsRequest recordsRequest, QueryStatusChecker queryStatusChecker) + { + boolean done = false; + ListClustersRequest request = new ListClustersRequest(); + + while (!done) { + ListClustersResult response = emr.listClusters(request); + + for (ClusterSummary next : response.getClusters()) { + Cluster cluster = null; + if (!next.getStatus().getState().toLowerCase().contains("terminated")) { + DescribeClusterResult clusterResponse = emr.describeCluster(new DescribeClusterRequest().withClusterId(next.getId())); + cluster = clusterResponse.getCluster(); + } + clusterToRow(next, cluster, spiller); + } + + request.setMarker(response.getMarker()); + + if (response.getMarker() == null || !queryStatusChecker.isQueryRunning()) { + done = true; + } + } + } + + /** + * Maps an EBS Volume into a row in our Apache Arrow response block(s). + * + * @param clusterSummary The CluserSummary for the provided Cluster. + * @param cluster The EMR Cluster to map. + * @param spiller The BlockSpiller to use when we want to write a matching row to the response. + * @note The current implementation is rather naive in how it maps fields. It leverages a static + * list of fields that we'd like to provide and then explicitly filters and converts each field. + */ + private void clusterToRow(ClusterSummary clusterSummary, + Cluster cluster, + BlockSpiller spiller) + { + spiller.writeRows((Block block, int row) -> { + boolean matched = true; + + matched &= block.offerValue("id", row, clusterSummary.getId()); + matched &= block.offerValue("name", row, clusterSummary.getName()); + matched &= block.offerValue("instance_hours", row, clusterSummary.getNormalizedInstanceHours()); + matched &= block.offerValue("state", row, clusterSummary.getStatus().getState()); + matched &= block.offerValue("state_code", row, clusterSummary.getStatus().getStateChangeReason().getCode()); + matched &= block.offerValue("state_msg", row, clusterSummary.getStatus().getStateChangeReason().getMessage()); + + if (cluster != null) { + matched &= block.offerValue("autoscaling_role", row, cluster.getAutoScalingRole()); + matched &= block.offerValue("custom_ami", row, cluster.getCustomAmiId()); + matched &= block.offerValue("instance_collection_type", row, cluster.getInstanceCollectionType()); + matched &= block.offerValue("log_uri", row, cluster.getLogUri()); + matched &= block.offerValue("master_public_dns", row, cluster.getMasterPublicDnsName()); + matched &= block.offerValue("release_label", row, cluster.getReleaseLabel()); + matched &= block.offerValue("running_ami", row, cluster.getRunningAmiVersion()); + matched &= block.offerValue("scale_down_behavior", row, cluster.getScaleDownBehavior()); + matched &= block.offerValue("service_role", row, cluster.getServiceRole()); + matched &= block.offerValue("service_role", row, cluster.getServiceRole()); + + List applications = cluster.getApplications().stream() + .map(next -> next.getName() + ":" + next.getVersion()).collect(Collectors.toList()); + matched &= block.offerComplexValue("applications", row, FieldResolver.DEFAULT, applications); + + List tags = cluster.getTags().stream() + .map(next -> next.getKey() + ":" + next.getValue()).collect(Collectors.toList()); + matched &= block.offerComplexValue("tags", row, FieldResolver.DEFAULT, tags); + } + + return matched ? 1 : 0; + }); + } + + /** + * Defines the schema of this table. + */ + static { + SCHEMA = SchemaBuilder.newBuilder() + .addStringField("id") + .addStringField("name") + .addIntField("instance_hours") + .addStringField("state") + .addStringField("state_code") + .addStringField("state_msg") + .addStringField("autoscaling_role") + .addStringField("custom_ami") + .addStringField("instance_collection_type") + .addStringField("log_uri") + .addStringField("master_public_dns") + .addStringField("release_label") + .addStringField("running_ami") + .addStringField("scale_down_behavior") + .addStringField("service_role") + .addListField("applications", Types.MinorType.VARCHAR.getType()) + .addListField("tags", Types.MinorType.VARCHAR.getType()) + .addMetadata("id", "Cluster Id") + .addMetadata("name", "Cluster Name") + .addMetadata("state", "State of the cluster.") + .addMetadata("state_code", "Code associated with the state of the cluster.") + .addMetadata("state_msg", "Message associated with the state of the cluster.") + .addMetadata("autoscaling_role", "AutoScaling role used by the cluster.") + .addMetadata("custom_ami", "Custom AMI used by the cluster (if any)") + .addMetadata("instance_collection_type", "Instance collection type used by the cluster.") + .addMetadata("log_uri", "URI where debug logs can be found for the cluster.") + .addMetadata("master_public_dns", "Public DNS name of the master node.") + .addMetadata("release_label", "EMR release label the cluster is running.") + .addMetadata("running_ami", "AMI the cluster are running.") + .addMetadata("scale_down_behavior", "Scale down behavoir of the cluster.") + .addMetadata("applications", "The EMR applications installed on the cluster.") + .addMetadata("tags", "Tags associated with the volume.") + .build(); + } +} diff --git a/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/RdsTableProvider.java b/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/RdsTableProvider.java new file mode 100644 index 0000000000..c8338bbeb8 --- /dev/null +++ b/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/RdsTableProvider.java @@ -0,0 +1,378 @@ +/*- + * #%L + * athena-aws-cmdb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.aws.cmdb.tables; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockSpiller; +import com.amazonaws.athena.connector.lambda.data.FieldBuilder; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.services.rds.AmazonRDS; +import com.amazonaws.services.rds.model.DBInstance; +import com.amazonaws.services.rds.model.DBInstanceStatusInfo; +import com.amazonaws.services.rds.model.DBParameterGroupStatus; +import com.amazonaws.services.rds.model.DBSecurityGroupMembership; +import com.amazonaws.services.rds.model.DBSubnetGroup; +import com.amazonaws.services.rds.model.DescribeDBInstancesRequest; +import com.amazonaws.services.rds.model.DescribeDBInstancesResult; +import com.amazonaws.services.rds.model.DomainMembership; +import com.amazonaws.services.rds.model.Endpoint; +import com.amazonaws.services.rds.model.Subnet; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.arrow.vector.types.pojo.Schema; + +import java.util.stream.Collectors; + +/** + * Maps your RDS instances to a table. + */ +public class RdsTableProvider + implements TableProvider +{ + private static final Schema SCHEMA; + private AmazonRDS rds; + + public RdsTableProvider(AmazonRDS rds) + { + this.rds = rds; + } + + /** + * @See TableProvider + */ + @Override + public String getSchema() + { + return "rds"; + } + + /** + * @See TableProvider + */ + @Override + public TableName getTableName() + { + return new TableName(getSchema(), "rds_instances"); + } + + /** + * @See TableProvider + */ + @Override + public GetTableResponse getTable(BlockAllocator blockAllocator, GetTableRequest getTableRequest) + { + return new GetTableResponse(getTableRequest.getCatalogName(), getTableName(), SCHEMA); + } + + /** + * Calls DescribeDBInstances on the AWS RDS Client returning all DB Instances that match the supplied predicate and attempting + * to push down certain predicates (namely queries for specific DB Instance) to EC2. + * + * @See TableProvider + */ + @Override + public void readWithConstraint(BlockSpiller spiller, ReadRecordsRequest recordsRequest, QueryStatusChecker queryStatusChecker) + { + boolean done = false; + DescribeDBInstancesRequest request = new DescribeDBInstancesRequest(); + + ValueSet idConstraint = recordsRequest.getConstraints().getSummary().get("instance_id"); + if (idConstraint != null && idConstraint.isSingleValue()) { + request.setDBInstanceIdentifier(idConstraint.getSingleValue().toString()); + } + + while (!done) { + DescribeDBInstancesResult response = rds.describeDBInstances(request); + + for (DBInstance instance : response.getDBInstances()) { + instanceToRow(instance, spiller); + } + + request.setMarker(response.getMarker()); + + if (response.getMarker() == null || !queryStatusChecker.isQueryRunning()) { + done = true; + } + } + } + + /** + * Maps a DBInstance into a row in our Apache Arrow response block(s). + * + * @param instance The DBInstance to map. + * @param spiller The BlockSpiller to use when we want to write a matching row to the response. + * @note The current implementation is rather naive in how it maps fields. It leverages a static + * list of fields that we'd like to provide and then explicitly filters and converts each field. + */ + private void instanceToRow(DBInstance instance, + BlockSpiller spiller) + { + spiller.writeRows((Block block, int row) -> { + boolean matched = true; + + matched &= block.offerValue("instance_id", row, instance.getDBInstanceIdentifier()); + matched &= block.offerValue("primary_az", row, instance.getAvailabilityZone()); + matched &= block.offerValue("storage_gb", row, instance.getAllocatedStorage()); + matched &= block.offerValue("is_encrypted", row, instance.getStorageEncrypted()); + matched &= block.offerValue("storage_type", row, instance.getStorageType()); + matched &= block.offerValue("backup_retention_days", row, instance.getBackupRetentionPeriod()); + matched &= block.offerValue("auto_upgrade", row, instance.getAutoMinorVersionUpgrade()); + matched &= block.offerValue("instance_class", row, instance.getDBInstanceClass()); + matched &= block.offerValue("port", row, instance.getDbInstancePort()); + matched &= block.offerValue("status", row, instance.getDBInstanceStatus()); + matched &= block.offerValue("dbi_resource_id", row, instance.getDbiResourceId()); + matched &= block.offerValue("name", row, instance.getDBName()); + matched &= block.offerValue("engine", row, instance.getEngine()); + matched &= block.offerValue("engine_version", row, instance.getEngineVersion()); + matched &= block.offerValue("license_model", row, instance.getLicenseModel()); + matched &= block.offerValue("secondary_az", row, instance.getSecondaryAvailabilityZone()); + matched &= block.offerValue("backup_window", row, instance.getPreferredBackupWindow()); + matched &= block.offerValue("maint_window", row, instance.getPreferredMaintenanceWindow()); + matched &= block.offerValue("read_replica_source_id", row, instance.getReadReplicaSourceDBInstanceIdentifier()); + matched &= block.offerValue("create_time", row, instance.getInstanceCreateTime()); + matched &= block.offerValue("public_access", row, instance.getPubliclyAccessible()); + matched &= block.offerValue("iops", row, instance.getIops()); + matched &= block.offerValue("is_multi_az", row, instance.getMultiAZ()); + + matched &= block.offerComplexValue("domains", row, (Field field, Object val) -> { + if (field.getName().equals("domain")) { + return ((DomainMembership) val).getDomain(); + } + else if (field.getName().equals("fqdn")) { + return ((DomainMembership) val).getFQDN(); + } + else if (field.getName().equals("iam_role")) { + return ((DomainMembership) val).getIAMRoleName(); + } + else if (field.getName().equals("status")) { + return ((DomainMembership) val).getStatus(); + } + + throw new RuntimeException("Unexpected field " + field.getName()); + }, + instance.getDomainMemberships()); + + matched &= block.offerComplexValue("param_groups", row, (Field field, Object val) -> { + if (field.getName().equals("name")) { + return ((DBParameterGroupStatus) val).getDBParameterGroupName(); + } + else if (field.getName().equals("status")) { + return ((DBParameterGroupStatus) val).getParameterApplyStatus(); + } + throw new RuntimeException("Unexpected field " + field.getName()); + }, + instance.getDBParameterGroups()); + + matched &= block.offerComplexValue("db_security_groups", + row, + (Field field, Object val) -> { + if (field.getName().equals("name")) { + return ((DBSecurityGroupMembership) val).getDBSecurityGroupName(); + } + else if (field.getName().equals("status")) { + return ((DBSecurityGroupMembership) val).getStatus(); + } + throw new RuntimeException("Unexpected field " + field.getName()); + }, + instance.getDBSecurityGroups()); + + matched &= block.offerComplexValue("subnet_group", + row, + (Field field, Object val) -> { + if (field.getName().equals("description")) { + return ((DBSubnetGroup) val).getDBSubnetGroupDescription(); + } + else if (field.getName().equals("name")) { + return ((DBSubnetGroup) val).getDBSubnetGroupName(); + } + else if (field.getName().equals("status")) { + return ((DBSubnetGroup) val).getSubnetGroupStatus(); + } + else if (field.getName().equals("vpc")) { + return ((DBSubnetGroup) val).getVpcId(); + } + else if (field.getName().equals("subnets")) { + return ((DBSubnetGroup) val).getSubnets().stream() + .map(next -> next.getSubnetIdentifier()).collect(Collectors.toList()); + } + else if (val instanceof Subnet) { + return ((Subnet) val).getSubnetIdentifier(); + } + throw new RuntimeException("Unexpected field " + field.getName()); + }, + instance.getDBSubnetGroup()); + + matched &= block.offerComplexValue("endpoint", + row, + (Field field, Object val) -> { + if (field.getName().equals("address")) { + return ((Endpoint) val).getAddress(); + } + else if (field.getName().equals("port")) { + return ((Endpoint) val).getPort(); + } + else if (field.getName().equals("zone")) { + return ((Endpoint) val).getHostedZoneId(); + } + throw new RuntimeException("Unexpected field " + field.getName()); + }, + instance.getEndpoint()); + + matched &= block.offerComplexValue("status_infos", + row, + (Field field, Object val) -> { + if (field.getName().equals("message")) { + return ((DBInstanceStatusInfo) val).getMessage(); + } + else if (field.getName().equals("is_normal")) { + return ((DBInstanceStatusInfo) val).getNormal(); + } + else if (field.getName().equals("status")) { + return ((DBInstanceStatusInfo) val).getStatus(); + } + else if (field.getName().equals("type")) { + return ((DBInstanceStatusInfo) val).getStatusType(); + } + throw new RuntimeException("Unexpected field " + field.getName()); + }, + instance.getStatusInfos()); + + return matched ? 1 : 0; + }); + } + + /** + * Defines the schema of this table. + */ + static { + SCHEMA = SchemaBuilder.newBuilder() + .addStringField("instance_id") + .addStringField("primary_az") + .addIntField("storage_gb") + .addBitField("is_encrypted") + .addStringField("storage_type") + .addIntField("backup_retention_days") + .addBitField("auto_upgrade") + .addStringField("instance_class") + .addIntField("port") + .addStringField("status") + .addStringField("dbi_resource_id") + .addStringField("name") + .addField( + FieldBuilder.newBuilder("domains", new ArrowType.List()) + .addField( + FieldBuilder.newBuilder("domain", Types.MinorType.STRUCT.getType()) + .addStringField("domain") + .addStringField("fqdn") + .addStringField("iam_role") + .addStringField("status") + .build()) + .build()) + .addStringField("engine") + .addStringField("engine_version") + .addStringField("license_model") + .addStringField("secondary_az") + .addStringField("backup_window") + .addStringField("maint_window") + .addStringField("read_replica_source_id") + .addField( + FieldBuilder.newBuilder("param_groups", new ArrowType.List()) + .addField( + FieldBuilder.newBuilder("param_group", Types.MinorType.STRUCT.getType()) + .addStringField("name") + .addStringField("status") + .build()) + .build()) + .addField( + FieldBuilder.newBuilder("db_security_groups", new ArrowType.List()) + .addField( + FieldBuilder.newBuilder("db_security_group", Types.MinorType.STRUCT.getType()) + .addStringField("name") + .addStringField("status") + .build()) + .build()) + .addStructField("subnet_group") + .addChildField("subnet_group", "name", Types.MinorType.VARCHAR.getType()) + .addChildField("subnet_group", "status", Types.MinorType.VARCHAR.getType()) + .addChildField("subnet_group", "vpc", Types.MinorType.VARCHAR.getType()) + .addChildField("subnet_group", FieldBuilder.newBuilder("subnets", Types.MinorType.LIST.getType()) + .addStringField("subnets").build()) + .addField(FieldBuilder.newBuilder("endpoint", Types.MinorType.STRUCT.getType()) + .addStringField("address") + .addIntField("port") + .addStringField("zone") + .build()) + .addField("create_time", Types.MinorType.DATEMILLI.getType()) + .addBitField("public_access") + + .addField( + FieldBuilder.newBuilder("status_infos", new ArrowType.List()) + .addField( + FieldBuilder.newBuilder("status_info", Types.MinorType.STRUCT.getType()) + .addStringField("message") + .addBitField("is_normal") + .addStringField("status") + .addStringField("type") + .build()) + .build()) + + .addIntField("iops") + .addBitField("is_multi_az") + .addMetadata("instance_id", "Database Instance Id") + .addMetadata("primary_az", "The primary az for the database instance") + .addMetadata("storage_gb", "Total allocated storage for the Database Instances in GB.") + .addMetadata("is_encrypted", "True if the database is encrypted.") + .addMetadata("storage_type", "The type of storage used by this Database Instance.") + .addMetadata("backup_retention_days", "The number of days of backups to keep.") + .addMetadata("auto_upgrade", "True if the cluster auto-upgrades minor versions.") + .addMetadata("instance_class", "The instance type used by this database.") + .addMetadata("port", "Listen port for the database.") + .addMetadata("status", "Status of the DB Instance.") + .addMetadata("dbi_resource_id", "Unique id for the instance of the database.") + .addMetadata("name", "Name of the DB Instance.") + .addMetadata("domains", "Active Directory domains to which the DB Instance is associated.") + .addMetadata("applications", "The EMR applications installed on the cluster.") + .addMetadata("engine", "The engine type of the DB Instance.") + .addMetadata("engine_version", "The engine version of the DB Instance") + .addMetadata("license_model", "The license model of the DB Instance") + .addMetadata("secondary_az", "The secondary AZ of the DB Instance") + .addMetadata("backup_window", "The backup window of the DB Instance") + .addMetadata("maint_window", "The maintenance window of the DB Instance") + .addMetadata("read_replica_source_id", "The read replica source id, if present, of the DB Instance") + .addMetadata("param_groups", "The param groups applied to the DB Instance") + .addMetadata("db_security_groups", "The security groups applies the DB Instance") + .addMetadata("subnet_groups", "The subnets available to the DB Instance") + .addMetadata("endpoint", "The endpoint of the DB Instance") + .addMetadata("create_time", "The create time of the DB Instance") + .addMetadata("public_access", "True if publically accessible.") + .addMetadata("status_infos", "The status info details associated with the DB Instance") + .addMetadata("iops", "The total provisioned IOPs for the DB Instance.") + .addMetadata("is_multi_az", "True if the DB Instance is avialable in multiple AZs.") + .build(); + } +} diff --git a/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/TableProvider.java b/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/TableProvider.java new file mode 100644 index 0000000000..dd2a3d7a25 --- /dev/null +++ b/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/TableProvider.java @@ -0,0 +1,88 @@ +/*- + * #%L + * athena-aws-cmdb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.aws.cmdb.tables; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockSpiller; +import com.amazonaws.athena.connector.lambda.data.BlockWriter; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; + +/** + * Defines the functionality required to supply the metadata and data required for the Athena AWS CMDB connector to + * to allow SQL queries to run over the virtual table. + */ +public interface TableProvider +{ + /** + * The schema name (aka database) that this table provider's table belongs to. + * + * @return String containing the schema name. + */ + String getSchema(); + + /** + * The fully qualified name of the table represented by this TableProvider. + * + * @return The TableName containing the fully qualified name of the Table. + */ + TableName getTableName(); + + /** + * Provides access to the Schema details of the requested table. + * + * @See MetadataHandler + */ + GetTableResponse getTable(BlockAllocator blockAllocator, GetTableRequest getTableRequest); + + /** + * Default implementation returns a single partition since many of the TableProviders may not support + * parallel scans. + * + * @See MetadataHandler + */ + default void getPartitions(BlockWriter blockWriter, GetTableLayoutRequest request) + throws Exception + { + //NoOp as we do not support partitioning. + } + + /** + * Default implementation does not enhance the partition results schema + * + * @See MetadataHandler + */ + default void enhancePartitionSchema(SchemaBuilder partitionSchemaBuilder, GetTableLayoutRequest request) + { + //NoOp as we do not support partitioning or added partition data + } + + /** + * Effects the requested read against the table, writing result row data using the supplied BlockSpliller. + * + * @See RecordHandler + */ + void readWithConstraint(BlockSpiller spiller, ReadRecordsRequest recordsRequest, QueryStatusChecker queryStatusChecker); +} diff --git a/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/EbsTableProvider.java b/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/EbsTableProvider.java new file mode 100644 index 0000000000..48b6503757 --- /dev/null +++ b/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/EbsTableProvider.java @@ -0,0 +1,199 @@ +/*- + * #%L + * athena-aws-cmdb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.aws.cmdb.tables.ec2; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockSpiller; +import com.amazonaws.athena.connector.lambda.data.FieldResolver; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.athena.connectors.aws.cmdb.tables.TableProvider; +import com.amazonaws.services.ec2.AmazonEC2; +import com.amazonaws.services.ec2.model.DescribeVolumesRequest; +import com.amazonaws.services.ec2.model.DescribeVolumesResult; +import com.amazonaws.services.ec2.model.Volume; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Schema; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Maps your EBS volumes to a table. + */ +public class EbsTableProvider + implements TableProvider +{ + private static final Logger logger = LoggerFactory.getLogger(EbsTableProvider.class); + private static final Schema SCHEMA; + private AmazonEC2 ec2; + + public EbsTableProvider(AmazonEC2 ec2) + { + this.ec2 = ec2; + } + + /** + * @See TableProvider + */ + @Override + public String getSchema() + { + return "ec2"; + } + + /** + * @See TableProvider + */ + @Override + public TableName getTableName() + { + return new TableName(getSchema(), "ebs_volumes"); + } + + /** + * @See TableProvider + */ + @Override + public GetTableResponse getTable(BlockAllocator blockAllocator, GetTableRequest getTableRequest) + { + return new GetTableResponse(getTableRequest.getCatalogName(), getTableName(), SCHEMA); + } + + /** + * Calls DescribeVolumes on the AWS EC2 Client returning all volumes that match the supplied predicate and attempting + * to push down certain predicates (namely queries for specific volumes) to EC2. + * + * @See TableProvider + */ + @Override + public void readWithConstraint(BlockSpiller spiller, ReadRecordsRequest recordsRequest, QueryStatusChecker queryStatusChecker) + { + boolean done = false; + DescribeVolumesRequest request = new DescribeVolumesRequest(); + + ValueSet idConstraint = recordsRequest.getConstraints().getSummary().get("id"); + if (idConstraint != null && idConstraint.isSingleValue()) { + request.setVolumeIds(Collections.singletonList(idConstraint.getSingleValue().toString())); + } + + while (!done) { + DescribeVolumesResult response = ec2.describeVolumes(request); + + for (Volume volume : response.getVolumes()) { + logger.info("readWithConstraint: {}", response); + instanceToRow(volume, spiller); + } + + request.setNextToken(response.getNextToken()); + + if (response.getNextToken() == null || !queryStatusChecker.isQueryRunning()) { + done = true; + } + } + } + + /** + * Maps an EBS Volume into a row in our Apache Arrow response block(s). + * + * @param volume The EBS Volume to map. + * @param spiller The BlockSpiller to use when we want to write a matching row to the response. + * @note The current implementation is rather naive in how it maps fields. It leverages a static + * list of fields that we'd like to provide and then explicitly filters and converts each field. + */ + private void instanceToRow(Volume volume, + BlockSpiller spiller) + { + spiller.writeRows((Block block, int row) -> { + boolean matched = true; + + matched &= block.offerValue("id", row, volume.getVolumeId()); + matched &= block.offerValue("type", row, volume.getVolumeType()); + matched &= block.offerValue("availability_zone", row, volume.getAvailabilityZone()); + matched &= block.offerValue("created_time", row, volume.getCreateTime()); + matched &= block.offerValue("is_encrypted", row, volume.getEncrypted()); + matched &= block.offerValue("kms_key_id", row, volume.getKmsKeyId()); + matched &= block.offerValue("size", row, volume.getSize()); + matched &= block.offerValue("iops", row, volume.getIops()); + matched &= block.offerValue("snapshot_id", row, volume.getSnapshotId()); + matched &= block.offerValue("state", row, volume.getState()); + + if (volume.getAttachments().size() == 1) { + matched &= block.offerValue("target", row, volume.getAttachments().get(0).getInstanceId()); + matched &= block.offerValue("attached_device", row, volume.getAttachments().get(0).getDevice()); + matched &= block.offerValue("attachment_state", row, volume.getAttachments().get(0).getState()); + matched &= block.offerValue("attachment_time", row, volume.getAttachments().get(0).getAttachTime()); + } + + List tags = volume.getTags().stream() + .map(next -> next.getKey() + ":" + next.getValue()).collect(Collectors.toList()); + matched &= block.offerComplexValue("tags", row, FieldResolver.DEFAULT, tags); + + return matched ? 1 : 0; + }); + } + + /** + * Defines the schema of this table. + */ + static { + SCHEMA = SchemaBuilder.newBuilder() + .addStringField("id") + .addStringField("type") + .addStringField("target") + .addStringField("attached_device") + .addStringField("attachment_state") + .addField("attachment_time", Types.MinorType.DATEMILLI.getType()) + .addStringField("availability_zone") + .addField("created_time", Types.MinorType.DATEMILLI.getType()) + .addBitField("is_encrypted") + .addStringField("kms_key_id") + .addIntField("size") + .addIntField("iops") + .addStringField("snapshot_id") + .addStringField("state") + .addListField("tags", Types.MinorType.VARCHAR.getType()) + .addMetadata("id", "EBS Volume Id") + .addMetadata("type", "EBS Volume Type") + .addMetadata("target", "EC2 Instance Id that this volume is attached to.") + .addMetadata("attached_device", "Device name where this EBS volume is attached.") + .addMetadata("attachment_state", "The state of the volume attachement.") + .addMetadata("attachment_time", "The time this volume was attached to its target.") + .addMetadata("availability_zone", "The AZ that this EBS Volume is in.") + .addMetadata("created_time", "The date time that the volume was created.") + .addMetadata("is_encrypted", "True if the volume is encrypted with KMS managed key.") + .addMetadata("kms_key_id", "The KMS key id used to encrypt this volume.") + .addMetadata("size", "The size in GBs of this volume.") + .addMetadata("iops", "Provisioned IOPs supported by this volume.") + .addMetadata("snapshot_id", "ID of the last snapshot for this volume.") + .addMetadata("state", "State of the EBS Volume.") + .addMetadata("tags", "Tags associated with the volume.") + .build(); + } +} diff --git a/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/Ec2TableProvider.java b/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/Ec2TableProvider.java new file mode 100644 index 0000000000..1bf2f7af49 --- /dev/null +++ b/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/Ec2TableProvider.java @@ -0,0 +1,313 @@ +/*- + * #%L + * athena-aws-cmdb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.aws.cmdb.tables.ec2; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockSpiller; +import com.amazonaws.athena.connector.lambda.data.FieldBuilder; +import com.amazonaws.athena.connector.lambda.data.FieldResolver; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.athena.connectors.aws.cmdb.tables.TableProvider; +import com.amazonaws.services.ec2.AmazonEC2; +import com.amazonaws.services.ec2.model.DescribeInstancesRequest; +import com.amazonaws.services.ec2.model.DescribeInstancesResult; +import com.amazonaws.services.ec2.model.Instance; +import com.amazonaws.services.ec2.model.InstanceNetworkInterface; +import com.amazonaws.services.ec2.model.InstanceState; +import com.amazonaws.services.ec2.model.Reservation; +import com.amazonaws.services.ec2.model.StateReason; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.arrow.vector.types.pojo.Schema; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Maps your EC2 instances to a table. + */ +public class Ec2TableProvider + implements TableProvider +{ + private static final Schema SCHEMA; + private AmazonEC2 ec2; + + public Ec2TableProvider(AmazonEC2 ec2) + { + this.ec2 = ec2; + } + + /** + * @See TableProvider + */ + @Override + public String getSchema() + { + return "ec2"; + } + + /** + * @See TableProvider + */ + @Override + public TableName getTableName() + { + return new TableName(getSchema(), "ec2_instances"); + } + + /** + * @See TableProvider + */ + @Override + public GetTableResponse getTable(BlockAllocator blockAllocator, GetTableRequest getTableRequest) + { + return new GetTableResponse(getTableRequest.getCatalogName(), getTableName(), SCHEMA); + } + + /** + * Calls DescribeInstances on the AWS EC2 Client returning all instances that match the supplied predicate and attempting + * to push down certain predicates (namely queries for specific ec2 instance) to EC2. + * + * @See TableProvider + */ + @Override + public void readWithConstraint(BlockSpiller spiller, ReadRecordsRequest recordsRequest, QueryStatusChecker queryStatusChecker) + { + boolean done = false; + DescribeInstancesRequest request = new DescribeInstancesRequest(); + + ValueSet idConstraint = recordsRequest.getConstraints().getSummary().get("instance_id"); + if (idConstraint != null && idConstraint.isSingleValue()) { + request.setInstanceIds(Collections.singletonList(idConstraint.getSingleValue().toString())); + } + + while (!done) { + DescribeInstancesResult response = ec2.describeInstances(request); + + for (Reservation reservation : response.getReservations()) { + for (Instance instance : reservation.getInstances()) { + instanceToRow(instance, spiller); + } + } + + request.setNextToken(response.getNextToken()); + + if (response.getNextToken() == null || !queryStatusChecker.isQueryRunning()) { + done = true; + } + } + } + + /** + * Maps an EC2 Instance into a row in our Apache Arrow response block(s). + * + * @param instance The EBS Volume to map. + * @param spiller The BlockSpiller to use when we want to write a matching row to the response. + * @note The current implementation is rather naive in how it maps fields. It leverages a static + * list of fields that we'd like to provide and then explicitly filters and converts each field. + */ + private void instanceToRow(Instance instance, + BlockSpiller spiller) + { + spiller.writeRows((Block block, int row) -> { + boolean matched = true; + + matched &= block.offerValue("instance_id", row, instance.getInstanceId()); + matched &= block.offerValue("image_id", row, instance.getImageId()); + matched &= block.offerValue("instance_type", row, instance.getInstanceType()); + matched &= block.offerValue("platform", row, instance.getPlatform()); + matched &= block.offerValue("private_dns_name", row, instance.getPrivateDnsName()); + matched &= block.offerValue("private_ip_address", row, instance.getPrivateIpAddress()); + matched &= block.offerValue("public_dns_name", row, instance.getPublicDnsName()); + matched &= block.offerValue("public_ip_address", row, instance.getPublicIpAddress()); + matched &= block.offerValue("subnet_id", row, instance.getSubnetId()); + matched &= block.offerValue("vpc_id", row, instance.getVpcId()); + matched &= block.offerValue("architecture", row, instance.getArchitecture()); + matched &= block.offerValue("instance_lifecycle", row, instance.getInstanceLifecycle()); + matched &= block.offerValue("root_device_name", row, instance.getRootDeviceName()); + matched &= block.offerValue("root_device_type", row, instance.getRootDeviceType()); + matched &= block.offerValue("spot_instance_request_id", row, instance.getSpotInstanceRequestId()); + matched &= block.offerValue("virtualization_type", row, instance.getVirtualizationType()); + matched &= block.offerValue("key_name", row, instance.getKeyName()); + matched &= block.offerValue("kernel_id", row, instance.getKernelId()); + matched &= block.offerValue("capacity_reservation_id", row, instance.getCapacityReservationId()); + matched &= block.offerValue("launch_time", row, instance.getLaunchTime()); + + matched &= block.offerComplexValue("state", + row, + (Field field, Object val) -> { + if (field.getName().equals("name")) { + return ((InstanceState) val).getName(); + } + else if (field.getName().equals("code")) { + return ((InstanceState) val).getCode(); + } + throw new RuntimeException("Unknown field " + field.getName()); + }, instance.getState()); + + matched &= block.offerComplexValue("network_interfaces", + row, + (Field field, Object val) -> { + if (field.getName().equals("status")) { + return ((InstanceNetworkInterface) val).getStatus(); + } + else if (field.getName().equals("subnet")) { + return ((InstanceNetworkInterface) val).getSubnetId(); + } + else if (field.getName().equals("vpc")) { + return ((InstanceNetworkInterface) val).getVpcId(); + } + else if (field.getName().equals("mac")) { + return ((InstanceNetworkInterface) val).getMacAddress(); + } + else if (field.getName().equals("private_dns")) { + return ((InstanceNetworkInterface) val).getPrivateDnsName(); + } + else if (field.getName().equals("private_ip")) { + return ((InstanceNetworkInterface) val).getPrivateIpAddress(); + } + else if (field.getName().equals("security_groups")) { + return ((InstanceNetworkInterface) val).getGroups().stream().map(next -> next.getGroupName() + ":" + next.getGroupId()).collect(Collectors.toList()); + } + else if (field.getName().equals("interface_id")) { + return ((InstanceNetworkInterface) val).getNetworkInterfaceId(); + } + + throw new RuntimeException("Unknown field " + field.getName()); + }, instance.getNetworkInterfaces()); + + matched &= block.offerComplexValue("state_reason", row, (Field field, Object val) -> { + if (field.getName().equals("message")) { + return ((StateReason) val).getMessage(); + } + else if (field.getName().equals("code")) { + return ((StateReason) val).getCode(); + } + throw new RuntimeException("Unknown field " + field.getName()); + }, instance.getStateReason()); + + matched &= block.offerValue("ebs_optimized", row, instance.getEbsOptimized()); + + List securityGroups = instance.getSecurityGroups().stream() + .map(next -> next.getGroupId()).collect(Collectors.toList()); + matched &= block.offerComplexValue("security_groups", row, FieldResolver.DEFAULT, securityGroups); + + List securityGroupNames = instance.getSecurityGroups().stream() + .map(next -> next.getGroupName()).collect(Collectors.toList()); + matched &= block.offerComplexValue("security_group_names", row, FieldResolver.DEFAULT, securityGroupNames); + + List ebsVolumes = instance.getBlockDeviceMappings().stream() + .map(next -> next.getEbs().getVolumeId()).collect(Collectors.toList()); + matched &= block.offerComplexValue("ebs_volumes", row, FieldResolver.DEFAULT, ebsVolumes); + + return matched ? 1 : 0; + }); + } + + /** + * Defines the schema of this table. + */ + static { + SCHEMA = SchemaBuilder.newBuilder() + .addStringField("instance_id") + .addStringField("image_id") + .addStringField("instance_type") + .addStringField("platform") + .addStringField("private_dns_name") + .addStringField("private_ip_address") + .addStringField("public_dns_name") + .addStringField("public_ip_address") + .addStringField("subnet_id") + .addStringField("vpc_id") + .addStringField("architecture") + .addStringField("instance_lifecycle") + .addStringField("root_device_name") + .addStringField("root_device_type") + .addStringField("spot_instance_request_id") + .addStringField("virtualization_type") + .addStringField("key_name") + .addStringField("kernel_id") + .addStringField("capacity_reservation_id") + .addField("launch_time", Types.MinorType.DATEMILLI.getType()) + .addStructField("state") + .addChildField("state", "name", Types.MinorType.VARCHAR.getType()) + .addChildField("state", "code", Types.MinorType.INT.getType()) + .addStructField("state_reason") + .addChildField("state_reason", "message", Types.MinorType.VARCHAR.getType()) + .addChildField("state_reason", "code", Types.MinorType.VARCHAR.getType()) + + //Example of a List of Structs + .addField( + FieldBuilder.newBuilder("network_interfaces", new ArrowType.List()) + .addField( + FieldBuilder.newBuilder("interface", Types.MinorType.STRUCT.getType()) + .addStringField("status") + .addStringField("subnet") + .addStringField("vpc") + .addStringField("mac") + .addStringField("private_dns") + .addStringField("private_ip") + .addListField("security_groups", Types.MinorType.VARCHAR.getType()) + .addStringField("interface_id") + .build()) + .build()) + .addBitField("ebs_optimized") + .addListField("security_groups", Types.MinorType.VARCHAR.getType()) + .addListField("security_group_names", Types.MinorType.VARCHAR.getType()) + .addListField("ebs_volumes", Types.MinorType.VARCHAR.getType()) + .addMetadata("instance_id", "EC2 Instance id.") + .addMetadata("image_id", "The id of the AMI used to boot the instance.") + .addMetadata("instance_type", "The EC2 instance type,") + .addMetadata("platform", "The platform of the instance (e.g. Linux)") + .addMetadata("private_dns_name", "The private dns name of the instance.") + .addMetadata("private_ip_address", "The private ip address of the instance.") + .addMetadata("public_dns_name", "The public dns name of the instance.") + .addMetadata("public_ip_address", "The public ip address of the instance.") + .addMetadata("subnet_id", "The subnet id that the instance was launched in.") + .addMetadata("vpc_id", "The id of the VPC that the instance was launched in.") + .addMetadata("architecture", "The architecture of the instance (e.g. x86).") + .addMetadata("instance_lifecycle", "The lifecycle state of the instance.") + .addMetadata("root_device_name", "The name of the root device that the instance booted from.") + .addMetadata("root_device_type", "The type of the root device that the instance booted from.") + .addMetadata("spot_instance_requestId", "Spot Request ID if the instance was launched via spot. ") + .addMetadata("virtualization_type", "The type of virtualization used by the instance (e.g. HVM)") + .addMetadata("key_name", "The name of the ec2 instance from the name tag.") + .addMetadata("kernel_id", "The id of the kernel used in the AMI that booted the instance.") + .addMetadata("capacity_reservation_id", "Capacity reservation id that this instance was launched against.") + .addMetadata("launch_time", "The time that the instance was launched at.") + .addMetadata("state", "The state of the ec2 instance.") + .addMetadata("state_reason", "The reason for the 'state' associated with the instance.") + .addMetadata("ebs_optimized", "True if the instance is EBS optimized.") + .addMetadata("network_interfaces", "The list of the network interfaces on the instance.") + .addMetadata("security_groups", "The list of security group (ids) attached to this instance.") + .addMetadata("security_group_names", "The list of security group (names) attached to this instance.") + .addMetadata("ebs_volumes", "The list of ebs volume (ids) attached to this instance.") + .build(); + } +} diff --git a/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/ImagesTableProvider.java b/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/ImagesTableProvider.java new file mode 100644 index 0000000000..8c7bc7a4e0 --- /dev/null +++ b/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/ImagesTableProvider.java @@ -0,0 +1,288 @@ +/*- + * #%L + * athena-aws-cmdb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.aws.cmdb.tables.ec2; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockSpiller; +import com.amazonaws.athena.connector.lambda.data.FieldBuilder; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.athena.connectors.aws.cmdb.tables.TableProvider; +import com.amazonaws.services.ec2.AmazonEC2; +import com.amazonaws.services.ec2.model.BlockDeviceMapping; +import com.amazonaws.services.ec2.model.DescribeImagesRequest; +import com.amazonaws.services.ec2.model.DescribeImagesResult; +import com.amazonaws.services.ec2.model.EbsBlockDevice; +import com.amazonaws.services.ec2.model.Image; +import com.amazonaws.services.ec2.model.Tag; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.arrow.vector.types.pojo.Schema; + +import java.util.Collections; +import java.util.List; + +/** + * Maps your EC2 images (aka AMIs) to a table. + */ +public class ImagesTableProvider + implements TableProvider +{ + private static final String DEFAULT_OWNER_ENV = "default_ec2_image_owner"; + private static final int MAX_IMAGES = 1000; + //Sets a default owner filter (when not null) to reduce the number of irrelevant AMIs returned when you do not + //query for a specific owner. + private static final String DEFAULT_OWNER = System.getenv(DEFAULT_OWNER_ENV); + private static final Schema SCHEMA; + private AmazonEC2 ec2; + + public ImagesTableProvider(AmazonEC2 ec2) + { + this.ec2 = ec2; + } + + /** + * @See TableProvider + */ + @Override + public String getSchema() + { + return "ec2"; + } + + /** + * @See TableProvider + */ + @Override + public TableName getTableName() + { + return new TableName(getSchema(), "ec2_images"); + } + + /** + * @See TableProvider + */ + @Override + public GetTableResponse getTable(BlockAllocator blockAllocator, GetTableRequest getTableRequest) + { + return new GetTableResponse(getTableRequest.getCatalogName(), getTableName(), SCHEMA); + } + + /** + * Calls DescribeImagess on the AWS EC2 Client returning all images that match the supplied predicate and attempting + * to push down certain predicates (namely queries for specific volumes) to EC2. + * + * @note Because of the large number of public AMIs we also support using a default 'owner' filter if your query doesn't + * filter on owner itself. You can set this using an env variable on your Lambda function defined by DEFAULT_OWNER_ENV. + * @See TableProvider + */ + @Override + public void readWithConstraint(BlockSpiller spiller, ReadRecordsRequest recordsRequest, QueryStatusChecker queryStatusChecker) + { + DescribeImagesRequest request = new DescribeImagesRequest(); + + ValueSet idConstraint = recordsRequest.getConstraints().getSummary().get("id"); + ValueSet ownerConstraint = recordsRequest.getConstraints().getSummary().get("owner"); + if (idConstraint != null && idConstraint.isSingleValue()) { + request.setImageIds(Collections.singletonList(idConstraint.getSingleValue().toString())); + } + else if (ownerConstraint != null && ownerConstraint.isSingleValue()) { + request.setOwners(Collections.singletonList(ownerConstraint.getSingleValue().toString())); + } + else if (DEFAULT_OWNER != null) { + request.setOwners(Collections.singletonList(DEFAULT_OWNER)); + } + else { + throw new RuntimeException("A default owner account must be set or the query must have owner" + + "in the where clause with exactly 1 value otherwise results may be too big."); + } + + DescribeImagesResult response = ec2.describeImages(request); + + int count = 0; + for (Image next : response.getImages()) { + if (count++ > MAX_IMAGES) { + throw new RuntimeException("Too many images returned, add an owner or id filter."); + } + instanceToRow(next, spiller); + } + } + + /** + * Maps an EC2 Image (AMI) into a row in our Apache Arrow response block(s). + * + * @param image The EC2 Image (AMI) to map. + * @param spiller The BlockSpiller to use when we want to write a matching row to the response. + * @note The current implementation is rather naive in how it maps fields. It leverages a static + * list of fields that we'd like to provide and then explicitly filters and converts each field. + */ + private void instanceToRow(Image image, + BlockSpiller spiller) + { + spiller.writeRows((Block block, int row) -> { + boolean matched = true; + + matched &= block.offerValue("id", row, image.getImageId()); + matched &= block.offerValue("architecture", row, image.getArchitecture()); + matched &= block.offerValue("created", row, image.getCreationDate()); + matched &= block.offerValue("description", row, image.getDescription()); + matched &= block.offerValue("hypervisor", row, image.getHypervisor()); + matched &= block.offerValue("location", row, image.getImageLocation()); + matched &= block.offerValue("type", row, image.getImageType()); + matched &= block.offerValue("kernel", row, image.getKernelId()); + matched &= block.offerValue("name", row, image.getName()); + matched &= block.offerValue("owner", row, image.getOwnerId()); + matched &= block.offerValue("platform", row, image.getPlatform()); + matched &= block.offerValue("ramdisk", row, image.getRamdiskId()); + matched &= block.offerValue("root_device", row, image.getRootDeviceName()); + matched &= block.offerValue("root_type", row, image.getRootDeviceType()); + matched &= block.offerValue("srvio_net", row, image.getSriovNetSupport()); + matched &= block.offerValue("state", row, image.getState()); + matched &= block.offerValue("virt_type", row, image.getVirtualizationType()); + matched &= block.offerValue("is_public", row, image.getPublic()); + + List tags = image.getTags(); + matched &= block.offerComplexValue("tags", + row, + (Field field, Object val) -> { + if (field.getName().equals("key")) { + return ((Tag) val).getKey(); + } + else if (field.getName().equals("value")) { + return ((Tag) val).getValue(); + } + + throw new RuntimeException("Unexpected field " + field.getName()); + }, + tags); + + matched &= block.offerComplexValue("block_devices", + row, + (Field field, Object val) -> { + if (field.getName().equals("dev_name")) { + return ((BlockDeviceMapping) val).getDeviceName(); + } + else if (field.getName().equals("no_device")) { + return ((BlockDeviceMapping) val).getNoDevice(); + } + else if (field.getName().equals("virt_name")) { + return ((BlockDeviceMapping) val).getVirtualName(); + } + else if (field.getName().equals("ebs")) { + return ((BlockDeviceMapping) val).getEbs(); + } + else if (field.getName().equals("ebs_size")) { + return ((EbsBlockDevice) val).getVolumeSize(); + } + else if (field.getName().equals("ebs_iops")) { + return ((EbsBlockDevice) val).getIops(); + } + else if (field.getName().equals("ebs_type")) { + return ((EbsBlockDevice) val).getVolumeType(); + } + else if (field.getName().equals("ebs_kms_key")) { + return ((EbsBlockDevice) val).getKmsKeyId(); + } + + throw new RuntimeException("Unexpected field " + field.getName()); + }, + image.getBlockDeviceMappings()); + + return matched ? 1 : 0; + }); + } + + /** + * Defines the schema of this table. + */ + static { + SCHEMA = SchemaBuilder.newBuilder() + .addStringField("id") + .addStringField("architecture") + .addStringField("created") + .addStringField("description") + .addStringField("hypervisor") + .addStringField("location") + .addStringField("type") + .addStringField("kernel") + .addStringField("name") + .addStringField("owner") + .addStringField("platform") + .addStringField("ramdisk") + .addStringField("root_device") + .addStringField("root_type") + .addStringField("srvio_net") + .addStringField("state") + .addStringField("virt_type") + .addBitField("is_public") + .addField( + FieldBuilder.newBuilder("tags", new ArrowType.List()) + .addField( + FieldBuilder.newBuilder("tag", Types.MinorType.STRUCT.getType()) + .addStringField("key") + .addStringField("value") + .build()) + .build()) + .addField( + FieldBuilder.newBuilder("block_devices", new ArrowType.List()) + .addField( + FieldBuilder.newBuilder("device", Types.MinorType.STRUCT.getType()) + .addStringField("dev_name") + .addStringField("no_device") + .addStringField("virt_name") + .addField( + FieldBuilder.newBuilder("ebs", Types.MinorType.STRUCT.getType()) + .addIntField("ebs_size") + .addIntField("ebs_iops") + .addStringField("ebs_type") + .addStringField("ebs_kms_key") + .build()) + .build()) + .build()) + .addMetadata("id", "The id of the image.") + .addMetadata("architecture", "The architecture required to run the image.") + .addMetadata("created", "The date and time the image was created.") + .addMetadata("description", "The description associated with the image.") + .addMetadata("hypervisor", "The type of hypervisor required by the image.") + .addMetadata("location", "The location of the image.") + .addMetadata("type", "The type of image.") + .addMetadata("kernel", "The kernel used by the image.") + .addMetadata("name", "The name of the image.") + .addMetadata("owner", "The owner of the image.") + .addMetadata("platform", "The platform required by the image.") + .addMetadata("ramdisk", "Detailed of the ram disk used by the image.") + .addMetadata("root_device", "The root device used by the image.") + .addMetadata("root_type", "The type of root device required by the image.") + .addMetadata("srvio_net", "Details of srvio network support in the image.") + .addMetadata("state", "The state of the image.") + .addMetadata("virt_type", "The type of virtualization supported by the image.") + .addMetadata("is_public", "True if the image is publically available.") + .addMetadata("tags", "Tags associated with the image.") + .addMetadata("block_devices", "Block devices required by the image.") + .build(); + } +} diff --git a/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/RouteTableProvider.java b/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/RouteTableProvider.java new file mode 100644 index 0000000000..24583be45e --- /dev/null +++ b/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/RouteTableProvider.java @@ -0,0 +1,215 @@ +/*- + * #%L + * athena-aws-cmdb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.aws.cmdb.tables.ec2; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockSpiller; +import com.amazonaws.athena.connector.lambda.data.FieldResolver; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.athena.connectors.aws.cmdb.tables.TableProvider; +import com.amazonaws.services.ec2.AmazonEC2; +import com.amazonaws.services.ec2.model.DescribeRouteTablesRequest; +import com.amazonaws.services.ec2.model.DescribeRouteTablesResult; +import com.amazonaws.services.ec2.model.Route; +import com.amazonaws.services.ec2.model.RouteTable; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Schema; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Maps your EC2 RouteTable entries (routes) to a table. + */ +public class RouteTableProvider + implements TableProvider +{ + private static final Schema SCHEMA; + private AmazonEC2 ec2; + + public RouteTableProvider(AmazonEC2 ec2) + { + this.ec2 = ec2; + } + + /** + * @See TableProvider + */ + @Override + public String getSchema() + { + return "ec2"; + } + + /** + * @See TableProvider + */ + @Override + public TableName getTableName() + { + return new TableName(getSchema(), "routing_tables"); + } + + /** + * @See TableProvider + */ + @Override + public GetTableResponse getTable(BlockAllocator blockAllocator, GetTableRequest getTableRequest) + { + return new GetTableResponse(getTableRequest.getCatalogName(), getTableName(), SCHEMA); + } + + /** + * Calls DescribeRouteTables on the AWS EC2 Client returning all Routes that match the supplied predicate and attempting + * to push down certain predicates (namely queries for specific RoutingTables) to EC2. + * + * @See TableProvider + */ + @Override + public void readWithConstraint(BlockSpiller spiller, ReadRecordsRequest recordsRequest, QueryStatusChecker queryStatusChecker) + { + boolean done = false; + DescribeRouteTablesRequest request = new DescribeRouteTablesRequest(); + + ValueSet idConstraint = recordsRequest.getConstraints().getSummary().get("route_table_id"); + if (idConstraint != null && idConstraint.isSingleValue()) { + request.setRouteTableIds(Collections.singletonList(idConstraint.getSingleValue().toString())); + } + + while (!done) { + DescribeRouteTablesResult response = ec2.describeRouteTables(request); + + for (RouteTable nextRouteTable : response.getRouteTables()) { + for (Route route : nextRouteTable.getRoutes()) { + instanceToRow(nextRouteTable, route, spiller); + } + } + + request.setNextToken(response.getNextToken()); + + if (response.getNextToken() == null || !queryStatusChecker.isQueryRunning()) { + done = true; + } + } + } + + /** + * Maps an EC2 Route into a row in our Apache Arrow response block(s). + * + * @param routeTable The RouteTable that owns the given Route. + * @param route The Route to map. + * @param spiller The BlockSpiller to use when we want to write a matching row to the response. + * @note The current implementation is rather naive in how it maps fields. It leverages a static + * list of fields that we'd like to provide and then explicitly filters and converts each field. + */ + private void instanceToRow(RouteTable routeTable, + Route route, + BlockSpiller spiller) + { + spiller.writeRows((Block block, int row) -> { + boolean matched = true; + + matched &= block.offerValue("route_table_id", row, routeTable.getRouteTableId()); + matched &= block.offerValue("owner", row, routeTable.getOwnerId()); + matched &= block.offerValue("vpc", row, routeTable.getVpcId()); + matched &= block.offerValue("dst_cidr", row, route.getDestinationCidrBlock()); + matched &= block.offerValue("dst_cidr_v6", row, route.getDestinationIpv6CidrBlock()); + matched &= block.offerValue("dst_prefix_list", row, route.getDestinationPrefixListId()); + matched &= block.offerValue("egress_igw", row, route.getEgressOnlyInternetGatewayId()); + matched &= block.offerValue("gateway", row, route.getGatewayId()); + matched &= block.offerValue("instance_id", row, route.getInstanceId()); + matched &= block.offerValue("instance_owner", row, route.getInstanceOwnerId()); + matched &= block.offerValue("nat_gateway", row, route.getNatGatewayId()); + matched &= block.offerValue("interface", row, route.getNetworkInterfaceId()); + matched &= block.offerValue("origin", row, route.getOrigin()); + matched &= block.offerValue("state", row, route.getState()); + matched &= block.offerValue("transit_gateway", row, route.getTransitGatewayId()); + matched &= block.offerValue("vpc_peering_con", row, route.getVpcPeeringConnectionId()); + + List associations = routeTable.getAssociations().stream() + .map(next -> next.getSubnetId() + ":" + next.getRouteTableId()).collect(Collectors.toList()); + matched &= block.offerComplexValue("associations", row, FieldResolver.DEFAULT, associations); + + List tags = routeTable.getTags().stream() + .map(next -> next.getKey() + ":" + next.getValue()).collect(Collectors.toList()); + matched &= block.offerComplexValue("tags", row, FieldResolver.DEFAULT, tags); + + List propagatingVgws = routeTable.getPropagatingVgws().stream() + .map(next -> next.getGatewayId()).collect(Collectors.toList()); + matched &= block.offerComplexValue("propagating_vgws", row, FieldResolver.DEFAULT, propagatingVgws); + + return matched ? 1 : 0; + }); + } + + /** + * Defines the schema of this table. + */ + static { + SCHEMA = SchemaBuilder.newBuilder() + .addStringField("route_table_id") + .addStringField("owner") + .addStringField("vpc") + .addListField("associations", Types.MinorType.VARCHAR.getType()) + .addListField("tags", Types.MinorType.VARCHAR.getType()) + .addListField("propagating_vgws", Types.MinorType.VARCHAR.getType()) + .addStringField("dst_cidr") + .addStringField("dst_cidr_v6") + .addStringField("dst_prefix_list") + .addStringField("egress_igw") + .addStringField("gateway") + .addStringField("instance_id") + .addStringField("instance_owner") + .addStringField("nat_gateway") + .addStringField("interface") + .addStringField("origin") + .addStringField("state") + .addStringField("transit_gateway") + .addStringField("vpc_peering_con") + .addMetadata("route_table_id", "Id of the route table the route belongs to.") + .addMetadata("owner", "Owner of the route table.") + .addMetadata("vpc", "VPC the route table is associated with.") + .addMetadata("associations", "List of associations for this route table.") + .addMetadata("tags", "Tags on the route table.") + .addMetadata("propagating_vgws", "Vgws the route table propogates through.") + .addMetadata("dst_cidr", "Destination IPv4 CIDR block for the route.") + .addMetadata("dst_cidr_v6", "Destination IPv6 CIDR block for the route.") + .addMetadata("dst_prefix_list", "Destination prefix list for the route.") + .addMetadata("egress_igw", "Egress gateway for the route.") + .addMetadata("gateway", "Gateway for the route.") + .addMetadata("instance_id", "Instance id of the route.") + .addMetadata("instance_owner", "Owner of the route.") + .addMetadata("nat_gateway", "NAT gateway used by the route.") + .addMetadata("interface", "Interface associated with the route.") + .addMetadata("origin", "Origin of the route.") + .addMetadata("state", "State of the route.") + .addMetadata("transit_gateway", "Transit Gateway associated with the route.") + .addMetadata("vpc_peering_con", "VPC Peering connection associated with the route.") + .build(); + } +} diff --git a/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/SecurityGroupsTableProvider.java b/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/SecurityGroupsTableProvider.java new file mode 100644 index 0000000000..8f4f6dd3c3 --- /dev/null +++ b/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/SecurityGroupsTableProvider.java @@ -0,0 +1,209 @@ +/*- + * #%L + * athena-aws-cmdb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.aws.cmdb.tables.ec2; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockSpiller; +import com.amazonaws.athena.connector.lambda.data.FieldResolver; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.athena.connectors.aws.cmdb.tables.TableProvider; +import com.amazonaws.services.ec2.AmazonEC2; +import com.amazonaws.services.ec2.model.DescribeSecurityGroupsRequest; +import com.amazonaws.services.ec2.model.DescribeSecurityGroupsResult; +import com.amazonaws.services.ec2.model.IpPermission; +import com.amazonaws.services.ec2.model.SecurityGroup; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Schema; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Maps your EC2 SecurityGroups to a table. + */ +public class SecurityGroupsTableProvider + implements TableProvider +{ + private static final String INGRESS = "ingress"; + private static final String EGRESS = "egress"; + + private static final Schema SCHEMA; + private AmazonEC2 ec2; + + public SecurityGroupsTableProvider(AmazonEC2 ec2) + { + this.ec2 = ec2; + } + + /** + * @See TableProvider + */ + @Override + public String getSchema() + { + return "ec2"; + } + + /** + * @See TableProvider + */ + @Override + public TableName getTableName() + { + return new TableName(getSchema(), "security_groups"); + } + + /** + * @See TableProvider + */ + @Override + public GetTableResponse getTable(BlockAllocator blockAllocator, GetTableRequest getTableRequest) + { + return new GetTableResponse(getTableRequest.getCatalogName(), getTableName(), SCHEMA); + } + + /** + * Calls DescribeSecurityGroups on the AWS EC2 Client returning all SecurityGroup rules that match the supplied + * predicate and attempting to push down certain predicates (namely queries for specific SecurityGroups) to EC2. + * + * @See TableProvider + */ + @Override + public void readWithConstraint(BlockSpiller spiller, ReadRecordsRequest recordsRequest, QueryStatusChecker queryStatusChecker) + { + boolean done = false; + DescribeSecurityGroupsRequest request = new DescribeSecurityGroupsRequest(); + + ValueSet idConstraint = recordsRequest.getConstraints().getSummary().get("id"); + if (idConstraint != null && idConstraint.isSingleValue()) { + request.setGroupIds(Collections.singletonList(idConstraint.getSingleValue().toString())); + } + + ValueSet nameConstraint = recordsRequest.getConstraints().getSummary().get("name"); + if (nameConstraint != null && nameConstraint.isSingleValue()) { + request.setGroupNames(Collections.singletonList(nameConstraint.getSingleValue().toString())); + } + + while (!done) { + DescribeSecurityGroupsResult response = ec2.describeSecurityGroups(request); + + //Each rule is mapped to a row in the response. SGs have INGRESS and EGRESS rules. + for (SecurityGroup next : response.getSecurityGroups()) { + for (IpPermission nextPerm : next.getIpPermissions()) { + instanceToRow(next, nextPerm, INGRESS, spiller); + } + + for (IpPermission nextPerm : next.getIpPermissionsEgress()) { + instanceToRow(next, nextPerm, EGRESS, spiller); + } + } + + request.setNextToken(response.getNextToken()); + if (response.getNextToken() == null || !queryStatusChecker.isQueryRunning()) { + done = true; + } + } + } + + /** + * Maps an each SecurityGroup rule (aka IpPermission) to a row in the response. + * + * @param securityGroup The SecurityGroup that owns the permission entry. + * @param permission The permission entry (aka rule) to map. + * @param direction The direction (EGRESS or INGRESS) of the rule. + * @param spiller The BlockSpiller to use when we want to write a matching row to the response. + * @note The current implementation is rather naive in how it maps fields. It leverages a static + * list of fields that we'd like to provide and then explicitly filters and converts each field. + */ + private void instanceToRow(SecurityGroup securityGroup, + IpPermission permission, + String direction, + BlockSpiller spiller) + { + spiller.writeRows((Block block, int row) -> { + boolean matched = true; + + matched &= block.offerValue("id", row, securityGroup.getGroupId()); + matched &= block.offerValue("name", row, securityGroup.getGroupName()); + matched &= block.offerValue("description", row, securityGroup.getDescription()); + matched &= block.offerValue("from_port", row, permission.getFromPort()); + matched &= block.offerValue("to_port", row, permission.getFromPort()); + matched &= block.offerValue("protocol", row, permission.getIpProtocol()); + matched &= block.offerValue("direction", row, permission.getIpProtocol()); + + List ipv4Ranges = permission.getIpv4Ranges().stream() + .map(next -> next.getCidrIp() + ":" + next.getDescription()).collect(Collectors.toList()); + matched &= block.offerComplexValue("ipv4_ranges", row, FieldResolver.DEFAULT, ipv4Ranges); + + List ipv6Ranges = permission.getIpv6Ranges().stream() + .map(next -> next.getCidrIpv6() + ":" + next.getDescription()).collect(Collectors.toList()); + matched &= block.offerComplexValue("ipv6_ranges", row, FieldResolver.DEFAULT, ipv6Ranges); + + List prefixLists = permission.getPrefixListIds().stream() + .map(next -> next.getPrefixListId() + ":" + next.getDescription()).collect(Collectors.toList()); + matched &= block.offerComplexValue("prefix_lists", row, FieldResolver.DEFAULT, prefixLists); + + List userIdGroups = permission.getUserIdGroupPairs().stream() + .map(next -> next.getUserId() + ":" + next.getGroupId()) + .collect(Collectors.toList()); + matched &= block.offerComplexValue("user_id_groups", row, FieldResolver.DEFAULT, userIdGroups); + + return matched ? 1 : 0; + }); + } + + /** + * Defines the schema of this table. + */ + static { + SCHEMA = SchemaBuilder.newBuilder() + .addStringField("id") + .addStringField("name") + .addStringField("description") + .addIntField("from_port") + .addIntField("to_port") + .addStringField("protocol") + .addStringField("direction") + .addListField("ipv4_ranges", Types.MinorType.VARCHAR.getType()) + .addListField("ipv6_ranges", Types.MinorType.VARCHAR.getType()) + .addListField("prefix_lists", Types.MinorType.VARCHAR.getType()) + .addListField("user_id_groups", Types.MinorType.VARCHAR.getType()) + .addMetadata("id", "Security Group ID.") + .addMetadata("name", "Name of the security group.") + .addMetadata("description", "Description of the security group.") + .addMetadata("from_port", "Beginging of the port range covered by this security group.") + .addMetadata("to_port", "Ending of the port range covered by this security group.") + .addMetadata("protocol", "The network protocol covered by this security group.") + .addMetadata("direction", "Notes if the rule applies inbound (ingress) or outbound (egress).") + .addMetadata("ipv4_ranges", "The ip v4 ranges covered by this security group.") + .addMetadata("ipv6_ranges", "The ip v6 ranges covered by this security group.") + .addMetadata("prefix_lists", "The prefix lists covered by this security group.") + .addMetadata("user_id_groups", "The user id groups covered by this security group.") + .build(); + } +} diff --git a/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/SubnetTableProvider.java b/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/SubnetTableProvider.java new file mode 100644 index 0000000000..f64bb9bd26 --- /dev/null +++ b/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/SubnetTableProvider.java @@ -0,0 +1,168 @@ +/*- + * #%L + * athena-aws-cmdb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.aws.cmdb.tables.ec2; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockSpiller; +import com.amazonaws.athena.connector.lambda.data.FieldResolver; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.athena.connectors.aws.cmdb.tables.TableProvider; +import com.amazonaws.services.ec2.AmazonEC2; +import com.amazonaws.services.ec2.model.DescribeSubnetsRequest; +import com.amazonaws.services.ec2.model.DescribeSubnetsResult; +import com.amazonaws.services.ec2.model.Subnet; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Schema; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Maps your EC2 Subnets to a table. + */ +public class SubnetTableProvider + implements TableProvider +{ + private static final Schema SCHEMA; + private AmazonEC2 ec2; + + public SubnetTableProvider(AmazonEC2 ec2) + { + this.ec2 = ec2; + } + + /** + * @See TableProvider + */ + @Override + public String getSchema() + { + return "ec2"; + } + + /** + * @See TableProvider + */ + @Override + public TableName getTableName() + { + return new TableName(getSchema(), "subnets"); + } + + /** + * @See TableProvider + */ + @Override + public GetTableResponse getTable(BlockAllocator blockAllocator, GetTableRequest getTableRequest) + { + return new GetTableResponse(getTableRequest.getCatalogName(), getTableName(), SCHEMA); + } + + /** + * Calls DescribeSubnets on the AWS EC2 Client returning all subnets that match the supplied predicate and attempting + * to push down certain predicates (namely queries for specific subnet) to EC2. + * + * @See TableProvider + */ + @Override + public void readWithConstraint(BlockSpiller spiller, ReadRecordsRequest recordsRequest, QueryStatusChecker queryStatusChecker) + { + DescribeSubnetsRequest request = new DescribeSubnetsRequest(); + + ValueSet idConstraint = recordsRequest.getConstraints().getSummary().get("id"); + if (idConstraint != null && idConstraint.isSingleValue()) { + request.setSubnetIds(Collections.singletonList(idConstraint.getSingleValue().toString())); + } + + DescribeSubnetsResult response = ec2.describeSubnets(request); + for (Subnet subnet : response.getSubnets()) { + instanceToRow(subnet, spiller); + } + } + + /** + * Maps an EC2 Subnet into a row in our Apache Arrow response block(s). + * + * @param subnet The EC2 Subnet to map. + * @param spiller The BlockSpiller to use when we want to write a matching row to the response. + * @note The current implementation is rather naive in how it maps fields. It leverages a static + * list of fields that we'd like to provide and then explicitly filters and converts each field. + */ + private void instanceToRow(Subnet subnet, + BlockSpiller spiller) + { + spiller.writeRows((Block block, int row) -> { + boolean matched = true; + + matched &= block.offerValue("id", row, subnet.getSubnetId()); + matched &= block.offerValue("availability_zone", row, subnet.getAvailabilityZone()); + matched &= block.offerValue("available_ip_count", row, subnet.getAvailableIpAddressCount()); + matched &= block.offerValue("cidr_block", row, subnet.getCidrBlock()); + matched &= block.offerValue("default_for_az", row, subnet.getDefaultForAz()); + matched &= block.offerValue("map_public_ip", row, subnet.getMapPublicIpOnLaunch()); + matched &= block.offerValue("owner", row, subnet.getOwnerId()); + matched &= block.offerValue("state", row, subnet.getState()); + matched &= block.offerValue("vpc", row, subnet.getVpcId()); + matched &= block.offerValue("vpc", row, subnet.getVpcId()); + + List tags = subnet.getTags().stream() + .map(next -> next.getKey() + ":" + next.getValue()).collect(Collectors.toList()); + matched &= block.offerComplexValue("tags", row, FieldResolver.DEFAULT, tags); + + return matched ? 1 : 0; + }); + } + + /** + * Defines the schema of this table. + */ + static { + SCHEMA = SchemaBuilder.newBuilder() + .addStringField("id") + .addStringField("availability_zone") + .addIntField("available_ip_count") + .addStringField("cidr_block") + .addBitField("default_for_az") + .addBitField("map_public_ip") + .addStringField("owner") + .addStringField("state") + .addListField("tags", Types.MinorType.VARCHAR.getType()) + .addStringField("vpc") + .addMetadata("id", "Subnet Id") + .addMetadata("availability_zone", "Availability zone the subnet is in.") + .addMetadata("available_ip_count", "Number of available IPs in the subnet.") + .addMetadata("cidr_block", "The CIDR block that the subnet uses to allocate addresses.") + .addMetadata("default_for_az", "True if this is the default subnet for the AZ.") + .addMetadata("map_public_ip", "True if public addresses are signed by default in this subnet.") + .addMetadata("owner", "Owner of the subnet.") + .addMetadata("state", "The state of the subnet.") + .addMetadata("vpc", "The VPC the subnet is part of.") + .addMetadata("tags", "Tags associated with the volume.") + .build(); + } +} diff --git a/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/VpcTableProvider.java b/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/VpcTableProvider.java new file mode 100644 index 0000000000..18087ba5e5 --- /dev/null +++ b/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/VpcTableProvider.java @@ -0,0 +1,161 @@ +/*- + * #%L + * athena-aws-cmdb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.aws.cmdb.tables.ec2; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockSpiller; +import com.amazonaws.athena.connector.lambda.data.FieldResolver; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.athena.connectors.aws.cmdb.tables.TableProvider; +import com.amazonaws.services.ec2.AmazonEC2; +import com.amazonaws.services.ec2.model.DescribeVpcsRequest; +import com.amazonaws.services.ec2.model.DescribeVpcsResult; +import com.amazonaws.services.ec2.model.Vpc; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Schema; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Maps your VPCs to a table. + */ +public class VpcTableProvider + implements TableProvider +{ + private static final Schema SCHEMA; + private AmazonEC2 ec2; + + public VpcTableProvider(AmazonEC2 ec2) + { + this.ec2 = ec2; + } + + /** + * @See TableProvider + */ + @Override + public String getSchema() + { + return "ec2"; + } + + /** + * @See TableProvider + */ + @Override + public TableName getTableName() + { + return new TableName(getSchema(), "vpcs"); + } + + /** + * @See TableProvider + */ + @Override + public GetTableResponse getTable(BlockAllocator blockAllocator, GetTableRequest getTableRequest) + { + return new GetTableResponse(getTableRequest.getCatalogName(), getTableName(), SCHEMA); + } + + /** + * Calls DescribeVPCs on the AWS EC2 Client returning all VPCs that match the supplied predicate and attempting + * to push down certain predicates (namely queries for specific VPCs) to EC2. + * + * @See TableProvider + */ + @Override + public void readWithConstraint(BlockSpiller spiller, ReadRecordsRequest recordsRequest, QueryStatusChecker queryStatusChecker) + { + DescribeVpcsRequest request = new DescribeVpcsRequest(); + + ValueSet idConstraint = recordsRequest.getConstraints().getSummary().get("id"); + if (idConstraint != null && idConstraint.isSingleValue()) { + request.setVpcIds(Collections.singletonList(idConstraint.getSingleValue().toString())); + } + + DescribeVpcsResult response = ec2.describeVpcs(request); + for (Vpc vpc : response.getVpcs()) { + instanceToRow(vpc, spiller); + } + } + + /** + * Maps a VPC into a row in our Apache Arrow response block(s). + * + * @param vpc The VPCs to map. + * @param spiller The BlockSpiller to use when we want to write a matching row to the response. + * @note The current implementation is rather naive in how it maps fields. It leverages a static + * list of fields that we'd like to provide and then explicitly filters and converts each field. + */ + private void instanceToRow(Vpc vpc, + BlockSpiller spiller) + { + spiller.writeRows((Block block, int row) -> { + boolean matched = true; + + matched &= block.offerValue("id", row, vpc.getVpcId()); + matched &= block.offerValue("cidr_block", row, vpc.getCidrBlock()); + matched &= block.offerValue("dhcp_opts", row, vpc.getDhcpOptionsId()); + matched &= block.offerValue("tenancy", row, vpc.getInstanceTenancy()); + matched &= block.offerValue("owner", row, vpc.getOwnerId()); + matched &= block.offerValue("state", row, vpc.getState()); + matched &= block.offerValue("is_default", row, vpc.getIsDefault()); + + List tags = vpc.getTags().stream() + .map(next -> next.getKey() + ":" + next.getValue()).collect(Collectors.toList()); + matched &= block.offerComplexValue("tags", row, FieldResolver.DEFAULT, tags); + + return matched ? 1 : 0; + }); + } + + /** + * Defines the schema of this table. + */ + static { + SCHEMA = SchemaBuilder.newBuilder() + .addStringField("id") + .addStringField("cidr_block") + .addStringField("dhcp_opts") + .addStringField("tenancy") + .addStringField("owner") + .addStringField("state") + .addBitField("is_default") + .addListField("tags", Types.MinorType.VARCHAR.getType()) + .addMetadata("id", "VPC Id") + .addMetadata("cidr_block", "CIDR block used to vend IPs for the VPC.") + .addMetadata("dhcp_opts", "DHCP options used for DNS resolution in the VPC.") + .addMetadata("tenancy", "EC2 Instance tenancy of this VPC (e.g. dedicated)") + .addMetadata("owner", "The owner of the VPC.") + .addMetadata("state", "The state of the VPC.") + .addMetadata("is_default", "True if the VPC is the default VPC.") + .addMetadata("tags", "Tags associated with the volume.") + .build(); + } +} diff --git a/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/s3/S3BucketsTableProvider.java b/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/s3/S3BucketsTableProvider.java new file mode 100644 index 0000000000..0387ac6bf7 --- /dev/null +++ b/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/s3/S3BucketsTableProvider.java @@ -0,0 +1,133 @@ +/*- + * #%L + * athena-aws-cmdb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.aws.cmdb.tables.s3; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockSpiller; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.athena.connectors.aws.cmdb.tables.TableProvider; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.Bucket; +import com.amazonaws.services.s3.model.Owner; +import org.apache.arrow.vector.types.pojo.Schema; + +/** + * Maps your S3 Objects to a table. + */ +public class S3BucketsTableProvider + implements TableProvider +{ + private static final Schema SCHEMA; + private AmazonS3 amazonS3; + + public S3BucketsTableProvider(AmazonS3 amazonS3) + { + this.amazonS3 = amazonS3; + } + + /** + * @See TableProvider + */ + @Override + public String getSchema() + { + return "s3"; + } + + /** + * @See TableProvider + */ + @Override + public TableName getTableName() + { + return new TableName(getSchema(), "buckets"); + } + + /** + * @See TableProvider + */ + @Override + public GetTableResponse getTable(BlockAllocator blockAllocator, GetTableRequest getTableRequest) + { + return new GetTableResponse(getTableRequest.getCatalogName(), getTableName(), SCHEMA); + } + + /** + * Calls DescribeDBInstances on the AWS RDS Client returning all DB Instances that match the supplied predicate and attempting + * to push down certain predicates (namely queries for specific DB Instance) to EC2. + * + * @See TableProvider + */ + @Override + public void readWithConstraint(BlockSpiller spiller, ReadRecordsRequest recordsRequest, QueryStatusChecker queryStatusChecker) + { + for (Bucket next : amazonS3.listBuckets()) { + toRow(next, spiller); + } + } + + /** + * Maps a DBInstance into a row in our Apache Arrow response block(s). + * + * @param bucket The S3 Bucket to map. + * @param spiller The BlockSpiller to use when we want to write a matching row to the response. + * @note The current implementation is rather naive in how it maps fields. It leverages a static + * list of fields that we'd like to provide and then explicitly filters and converts each field. + */ + private void toRow(Bucket bucket, + BlockSpiller spiller) + { + spiller.writeRows((Block block, int row) -> { + boolean matched = true; + matched &= block.offerValue("bucket_name", row, bucket.getName()); + matched &= block.offerValue("create_date", row, bucket.getCreationDate()); + + Owner owner = bucket.getOwner(); + if (owner != null) { + matched &= block.offerValue("owner_name", row, bucket.getOwner().getDisplayName()); + matched &= block.offerValue("owner_id", row, bucket.getOwner().getId()); + } + + return matched ? 1 : 0; + }); + } + + /** + * Defines the schema of this table. + */ + static { + SCHEMA = SchemaBuilder.newBuilder() + .addStringField("bucket_name") + .addDateMilliField("create_date") + .addStringField("owner_name") + .addStringField("owner_id") + .addMetadata("bucket_name", "The name of the bucket that this object is in.") + .addMetadata("create_date", "The time the bucket was created.") + .addMetadata("owner_name", "The owner name of the object.") + .addMetadata("owner_id", "The owner_id of the object.") + .build(); + } +} diff --git a/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/s3/S3ObjectsTableProvider.java b/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/s3/S3ObjectsTableProvider.java new file mode 100644 index 0000000000..c58315f49e --- /dev/null +++ b/athena-aws-cmdb/src/main/java/com/amazonaws/athena/connectors/aws/cmdb/tables/s3/S3ObjectsTableProvider.java @@ -0,0 +1,166 @@ +/*- + * #%L + * athena-aws-cmdb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.aws.cmdb.tables.s3; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockSpiller; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.athena.connectors.aws.cmdb.tables.TableProvider; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.ListObjectsV2Request; +import com.amazonaws.services.s3.model.ListObjectsV2Result; +import com.amazonaws.services.s3.model.Owner; +import com.amazonaws.services.s3.model.S3ObjectSummary; +import org.apache.arrow.vector.types.pojo.Schema; + +/** + * Maps your S3 Objects to a table. + */ +public class S3ObjectsTableProvider + implements TableProvider +{ + private static final int MAX_KEYS = 1000; + private static final Schema SCHEMA; + private AmazonS3 amazonS3; + + public S3ObjectsTableProvider(AmazonS3 amazonS3) + { + this.amazonS3 = amazonS3; + } + + /** + * @See TableProvider + */ + @Override + public String getSchema() + { + return "s3"; + } + + /** + * @See TableProvider + */ + @Override + public TableName getTableName() + { + return new TableName(getSchema(), "objects"); + } + + /** + * @See TableProvider + */ + @Override + public GetTableResponse getTable(BlockAllocator blockAllocator, GetTableRequest getTableRequest) + { + return new GetTableResponse(getTableRequest.getCatalogName(), getTableName(), SCHEMA); + } + + /** + * Calls DescribeDBInstances on the AWS RDS Client returning all DB Instances that match the supplied predicate and attempting + * to push down certain predicates (namely queries for specific DB Instance) to EC2. + * + * @See TableProvider + */ + @Override + public void readWithConstraint(BlockSpiller spiller, ReadRecordsRequest recordsRequest, QueryStatusChecker queryStatusChecker) + { + ValueSet bucketConstraint = recordsRequest.getConstraints().getSummary().get("bucket_name"); + String bucket; + if (bucketConstraint != null && bucketConstraint.isSingleValue()) { + bucket = bucketConstraint.getSingleValue().toString(); + } + else { + throw new IllegalArgumentException("Queries against the objects table must filter on a single bucket " + + "(e.g. where bucket_name='my_bucket'."); + } + + ListObjectsV2Request req = new ListObjectsV2Request().withBucketName(bucket).withMaxKeys(MAX_KEYS); + ListObjectsV2Result result; + do { + result = amazonS3.listObjectsV2(req); + for (S3ObjectSummary objectSummary : result.getObjectSummaries()) { + toRow(objectSummary, spiller); + } + req.setContinuationToken(result.getNextContinuationToken()); + } + while (result.isTruncated() && queryStatusChecker.isQueryRunning()); + } + + /** + * Maps a DBInstance into a row in our Apache Arrow response block(s). + * + * @param objectSummary The S3 ObjectSummary to map. + * @param spiller The BlockSpiller to use when we want to write a matching row to the response. + * @note The current implementation is rather naive in how it maps fields. It leverages a static + * list of fields that we'd like to provide and then explicitly filters and converts each field. + */ + private void toRow(S3ObjectSummary objectSummary, + BlockSpiller spiller) + { + spiller.writeRows((Block block, int row) -> { + boolean matched = true; + matched &= block.offerValue("bucket_name", row, objectSummary.getBucketName()); + matched &= block.offerValue("e_tag", row, objectSummary.getETag()); + matched &= block.offerValue("key", row, objectSummary.getKey()); + matched &= block.offerValue("bytes", row, objectSummary.getSize()); + matched &= block.offerValue("storage_class", row, objectSummary.getStorageClass()); + matched &= block.offerValue("last_modified", row, objectSummary.getLastModified()); + + Owner owner = objectSummary.getOwner(); + if (owner != null) { + matched &= block.offerValue("owner_name", row, owner.getDisplayName()); + matched &= block.offerValue("owner_id", row, owner.getId()); + } + + return matched ? 1 : 0; + }); + } + + /** + * Defines the schema of this table. + */ + static { + SCHEMA = SchemaBuilder.newBuilder() + .addStringField("bucket_name") + .addStringField("key") + .addStringField("e_tag") + .addBigIntField("bytes") + .addStringField("storage_class") + .addDateMilliField("last_modified") + .addStringField("owner_name") + .addStringField("owner_id") + .addMetadata("bucket_name", "The name of the bucket that this object is in.") + .addMetadata("key", "The key of the object.") + .addMetadata("e_tag", "eTag of the Object.") + .addMetadata("bytes", "The size of the object in bytes.") + .addMetadata("storage_class", "The storage class of the object.") + .addMetadata("last_modified", "The last time the object was modified.") + .addMetadata("owner_name", "The owner name of the object.") + .addMetadata("owner_id", "The owner_id of the object.") + .build(); + } +} diff --git a/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/AwsCmdbMetadataHandlerTest.java b/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/AwsCmdbMetadataHandlerTest.java new file mode 100644 index 0000000000..909ceda41d --- /dev/null +++ b/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/AwsCmdbMetadataHandlerTest.java @@ -0,0 +1,207 @@ +/*- + * #%L + * athena-aws-cmdb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.aws.cmdb; + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.amazonaws.athena.connector.lambda.data.BlockWriter; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.ConstraintEvaluator; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesResponse; +import com.amazonaws.athena.connector.lambda.security.FederatedIdentity; +import com.amazonaws.athena.connector.lambda.security.LocalKeyFactory; +import com.amazonaws.athena.connectors.aws.cmdb.tables.TableProvider; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class AwsCmdbMetadataHandlerTest +{ + private String catalog = "catalog"; + private String bucket = "bucket"; + private String prefix = "prefix"; + private String queryId = "queryId"; + private FederatedIdentity identity = new FederatedIdentity("id", "principal", "account"); + + @Mock + private AmazonS3 mockS3; + + @Mock + private TableProviderFactory mockTableProviderFactory; + + @Mock + private Constraints mockConstraints; + + @Mock + private TableProvider mockTableProvider1; + + @Mock + private TableProvider mockTableProvider2; + + @Mock + private TableProvider mockTableProvider3; + + private BlockAllocator blockAllocator; + + @Mock + private Block mockBlock; + + @Mock + private AWSSecretsManager mockSecretsManager; + + @Mock + private AmazonAthena mockAthena; + + private AwsCmdbMetadataHandler handler; + + @Before + public void setUp() + throws Exception + { + blockAllocator = new BlockAllocatorImpl(); + Map tableProviderMap = new HashMap<>(); + tableProviderMap.putIfAbsent(new TableName("schema1", "table1"), mockTableProvider1); + tableProviderMap.putIfAbsent(new TableName("schema1", "table2"), mockTableProvider2); + tableProviderMap.putIfAbsent(new TableName("schema2", "table1"), mockTableProvider3); + + when(mockTableProviderFactory.getTableProviders()).thenReturn(tableProviderMap); + + Map> schemas = new HashMap<>(); + schemas.put("schema1", new ArrayList<>()); + schemas.put("schema2", new ArrayList<>()); + schemas.get("schema1").add(new TableName("schema1", "table1")); + schemas.get("schema1").add(new TableName("schema1", "table2")); + schemas.get("schema2").add(new TableName("schema2", "table1")); + + when(mockTableProviderFactory.getSchemas()).thenReturn(schemas); + + handler = new AwsCmdbMetadataHandler(mockTableProviderFactory, new LocalKeyFactory(), mockSecretsManager, mockAthena, bucket, prefix); + + verify(mockTableProviderFactory, times(1)).getTableProviders(); + verify(mockTableProviderFactory, times(1)).getSchemas(); + verifyNoMoreInteractions(mockTableProviderFactory); + } + + @After + public void tearDown() + throws Exception + { + blockAllocator.close(); + } + + @Test + public void doListSchemaNames() + { + ListSchemasRequest request = new ListSchemasRequest(identity, queryId, catalog); + ListSchemasResponse response = handler.doListSchemaNames(blockAllocator, request); + + assertEquals(2, response.getSchemas().size()); + assertTrue(response.getSchemas().contains("schema1")); + assertTrue(response.getSchemas().contains("schema2")); + } + + @Test + public void doListTables() + { + ListTablesRequest request = new ListTablesRequest(identity, queryId, catalog, "schema1"); + ListTablesResponse response = handler.doListTables(blockAllocator, request); + + assertEquals(2, response.getTables().size()); + assertTrue(response.getTables().contains(new TableName("schema1", "table1"))); + assertTrue(response.getTables().contains(new TableName("schema1", "table2"))); + } + + @Test + public void doGetTable() + { + GetTableRequest request = new GetTableRequest(identity, queryId, catalog, new TableName("schema1", "table1")); + + when(mockTableProvider1.getTable(eq(blockAllocator), eq(request))).thenReturn(mock(GetTableResponse.class)); + GetTableResponse response = handler.doGetTable(blockAllocator, request); + + assertNotNull(response); + verify(mockTableProvider1, times(1)).getTable(eq(blockAllocator), eq(request)); + } + + @Test + public void doGetTableLayout() + throws Exception + { + GetTableLayoutRequest request = new GetTableLayoutRequest(identity, queryId, catalog, + new TableName("schema1", "table1"), + mockConstraints, + SchemaBuilder.newBuilder().build(), + Collections.EMPTY_SET); + + GetTableLayoutResponse response = handler.doGetTableLayout(blockAllocator, request); + + assertNotNull(response); + assertEquals(1, response.getPartitions().getRowCount()); + } + + @Test + public void doGetSplits() + { + GetSplitsRequest request = new GetSplitsRequest(identity, queryId, catalog, + new TableName("schema1", "table1"), + mockBlock, + Collections.emptyList(), + new Constraints(new HashMap<>()), + null); + + GetSplitsResponse response = handler.doGetSplits(blockAllocator, request); + + assertNotNull(response); + } +} diff --git a/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/AwsCmdbRecordHandlerTest.java b/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/AwsCmdbRecordHandlerTest.java new file mode 100644 index 0000000000..5015515721 --- /dev/null +++ b/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/AwsCmdbRecordHandlerTest.java @@ -0,0 +1,124 @@ +/*- + * #%L + * athena-aws-cmdb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.aws.cmdb; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.BlockSpiller; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.ConstraintEvaluator; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.domain.spill.S3SpillLocation; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.athena.connector.lambda.security.EncryptionKeyFactory; +import com.amazonaws.athena.connector.lambda.security.FederatedIdentity; +import com.amazonaws.athena.connector.lambda.security.LocalKeyFactory; +import com.amazonaws.athena.connectors.aws.cmdb.tables.TableProvider; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import java.util.Collections; +import java.util.UUID; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class AwsCmdbRecordHandlerTest +{ + private String bucket = "bucket"; + private String prefix = "prefix"; + private EncryptionKeyFactory keyFactory = new LocalKeyFactory(); + private FederatedIdentity identity = new FederatedIdentity("id", "principal", "account"); + + @Mock + private AmazonS3 mockS3; + + @Mock + private TableProviderFactory mockTableProviderFactory; + + @Mock + private ConstraintEvaluator mockEvaluator; + + @Mock + private BlockSpiller mockBlockSpiller; + + @Mock + private TableProvider mockTableProvider; + + @Mock + private AWSSecretsManager mockSecretsManager; + + @Mock + private AmazonAthena mockAthena; + + @Mock + private QueryStatusChecker queryStatusChecker; + + private AwsCmdbRecordHandler handler; + + @Before + public void setUp() + throws Exception + { + when(mockTableProviderFactory.getTableProviders()) + .thenReturn(Collections.singletonMap(new TableName("schema", "table"), mockTableProvider)); + + handler = new AwsCmdbRecordHandler(mockS3, mockSecretsManager, mockAthena, mockTableProviderFactory); + + verify(mockTableProviderFactory, times(1)).getTableProviders(); + verifyNoMoreInteractions(mockTableProviderFactory); + + when(queryStatusChecker.isQueryRunning()).thenReturn(true); + } + + @Test + public void readWithConstraint() + { + ReadRecordsRequest request = new ReadRecordsRequest(identity, "catalog", + "queryId", + new TableName("schema", "table"), + SchemaBuilder.newBuilder().build(), + Split.newBuilder(S3SpillLocation.newBuilder() + .withBucket(bucket) + .withSplitId(UUID.randomUUID().toString()) + .withQueryId(UUID.randomUUID().toString()) + .withIsDirectory(true) + .build(), keyFactory.create()).build(), + new Constraints(Collections.EMPTY_MAP), + 100_000, + 100_000); + + handler.readWithConstraint(mockBlockSpiller, request, queryStatusChecker); + + verify(mockTableProvider, times(1)).readWithConstraint(any(BlockSpiller.class), eq(request), eq(queryStatusChecker)); + } +} diff --git a/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/TableProviderFactoryTest.java b/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/TableProviderFactoryTest.java new file mode 100644 index 0000000000..cea1c54fa4 --- /dev/null +++ b/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/TableProviderFactoryTest.java @@ -0,0 +1,85 @@ +/*- + * #%L + * athena-aws-cmdb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.aws.cmdb; + +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connectors.aws.cmdb.tables.TableProvider; +import com.amazonaws.services.ec2.AmazonEC2; +import com.amazonaws.services.elasticmapreduce.AmazonElasticMapReduce; +import com.amazonaws.services.rds.AmazonRDS; +import com.amazonaws.services.s3.AmazonS3; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.*; + +@RunWith(MockitoJUnitRunner.class) +public class TableProviderFactoryTest +{ + private int expectedSchemas = 4; + private int expectedTables = 11; + + @Mock + private AmazonEC2 mockEc2; + + @Mock + private AmazonElasticMapReduce mockEmr; + + @Mock + private AmazonRDS mockRds; + + @Mock + private AmazonS3 amazonS3; + + private TableProviderFactory factory = new TableProviderFactory(mockEc2, mockEmr, mockRds, amazonS3); + + @Test + public void getTableProviders() + { + int count = 0; + for (Map.Entry next : factory.getTableProviders().entrySet()) { + assertEquals(next.getKey(), next.getValue().getTableName()); + assertEquals(next.getKey().getSchemaName(), next.getValue().getSchema()); + count++; + } + assertEquals(expectedTables, count); + } + + @Test + public void getSchemas() + { + int schemas = 0; + int tables = 0; + for (Map.Entry> next : factory.getSchemas().entrySet()) { + for (TableName nextTableName : next.getValue()) { + assertEquals(next.getKey(), nextTableName.getSchemaName()); + tables++; + } + schemas++; + } + assertEquals(expectedSchemas, schemas); + assertEquals(expectedTables, tables); + } +} diff --git a/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/AbstractTableProviderTest.java b/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/AbstractTableProviderTest.java new file mode 100644 index 0000000000..9ed15516fd --- /dev/null +++ b/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/AbstractTableProviderTest.java @@ -0,0 +1,262 @@ +/*- + * #%L + * athena-aws-cmdb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.aws.cmdb.tables; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.amazonaws.athena.connector.lambda.data.BlockSpiller; +import com.amazonaws.athena.connector.lambda.data.BlockUtils; +import com.amazonaws.athena.connector.lambda.data.S3BlockSpillReader; +import com.amazonaws.athena.connector.lambda.data.S3BlockSpiller; +import com.amazonaws.athena.connector.lambda.data.SpillConfig; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.ConstraintEvaluator; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.domain.predicate.EquatableValueSet; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import com.amazonaws.athena.connector.lambda.domain.spill.S3SpillLocation; +import com.amazonaws.athena.connector.lambda.domain.spill.SpillLocation; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.athena.connector.lambda.security.EncryptionKey; +import com.amazonaws.athena.connector.lambda.security.EncryptionKeyFactory; +import com.amazonaws.athena.connector.lambda.security.FederatedIdentity; +import com.amazonaws.athena.connector.lambda.security.LocalKeyFactory; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.PutObjectResult; +import com.amazonaws.services.s3.model.S3Object; +import com.amazonaws.services.s3.model.S3ObjectInputStream; +import com.google.common.io.ByteStreams; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Schema; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.runners.MockitoJUnitRunner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public abstract class AbstractTableProviderTest +{ + private static final Logger logger = LoggerFactory.getLogger(AbstractTableProviderTest.class); + + private BlockAllocator allocator; + + private FederatedIdentity identity = new FederatedIdentity("id", "principal", "account"); + private String idField = getIdField(); + private String idValue = getIdValue(); + private String expectedQuery = "queryId"; + private String expectedCatalog = "catalog"; + private String expectedSchema = getExpectedSchema(); + private String expectedTable = getExpectedTable(); + private TableName expectedTableName = new TableName(expectedSchema, expectedTable); + + private TableProvider provider; + + private final List mockS3Store = new ArrayList<>(); + + @Mock + private AmazonS3 amazonS3; + + @Mock + private QueryStatusChecker queryStatusChecker; + + private S3BlockSpillReader blockSpillReader; + + private EncryptionKeyFactory keyFactory = new LocalKeyFactory(); + + protected abstract String getIdField(); + + protected abstract String getIdValue(); + + protected abstract String getExpectedSchema(); + + protected abstract String getExpectedTable(); + + protected abstract TableProvider setUpSource(); + + protected abstract void setUpRead(); + + protected abstract int getExpectedRows(); + + protected abstract void validateRow(Block block, int pos); + + @Before + public void setUp() + { + allocator = new BlockAllocatorImpl(); + + when(amazonS3.putObject(anyObject(), anyObject(), anyObject(), anyObject())) + .thenAnswer((InvocationOnMock invocationOnMock) -> { + InputStream inputStream = (InputStream) invocationOnMock.getArguments()[2]; + ByteHolder byteHolder = new ByteHolder(); + byteHolder.setBytes(ByteStreams.toByteArray(inputStream)); + mockS3Store.add(byteHolder); + return mock(PutObjectResult.class); + }); + + when(amazonS3.getObject(anyString(), anyString())) + .thenAnswer((InvocationOnMock invocationOnMock) -> { + S3Object mockObject = mock(S3Object.class); + ByteHolder byteHolder = mockS3Store.get(0); + mockS3Store.remove(0); + when(mockObject.getObjectContent()).thenReturn( + new S3ObjectInputStream( + new ByteArrayInputStream(byteHolder.getBytes()), null)); + return mockObject; + }); + + blockSpillReader = new S3BlockSpillReader(amazonS3, allocator); + + provider = setUpSource(); + + when(queryStatusChecker.isQueryRunning()).thenReturn(true); + } + + @After + public void after() + { + mockS3Store.clear(); + allocator.close(); + } + + @Test + public void getSchema() + { + assertEquals(expectedSchema, provider.getSchema()); + } + + @Test + public void getTableName() + { + assertEquals(expectedTableName, provider.getTableName()); + } + + @Test + public void readTableTest() + { + GetTableRequest request = new GetTableRequest(identity, expectedQuery, expectedCatalog, expectedTableName); + GetTableResponse response = provider.getTable(allocator, request); + assertTrue(response.getSchema().getFields().size() > 1); + + Map constraintsMap = new HashMap<>(); + + constraintsMap.put(idField, + EquatableValueSet.newBuilder(allocator, Types.MinorType.VARCHAR.getType(), true, false) + .add(idValue).build()); + + Constraints constraints = new Constraints(constraintsMap); + + ConstraintEvaluator evaluator = new ConstraintEvaluator(allocator, response.getSchema(), constraints); + + S3SpillLocation spillLocation = S3SpillLocation.newBuilder() + .withBucket("bucket") + .withPrefix("prefix") + .withSplitId(UUID.randomUUID().toString()) + .withQueryId(UUID.randomUUID().toString()) + .withIsDirectory(true) + .build(); + + ReadRecordsRequest readRequest = new ReadRecordsRequest(identity, + expectedCatalog, + "queryId", + expectedTableName, + response.getSchema(), + Split.newBuilder(spillLocation, keyFactory.create()).build(), + constraints, + 100_000_000, + 100_000_000); + + SpillConfig spillConfig = SpillConfig.newBuilder() + .withSpillLocation(spillLocation) + .withMaxBlockBytes(3_000_000) + .withMaxInlineBlockBytes(0) + .withRequestId("queryid") + .withEncryptionKey(keyFactory.create()) + .build(); + + setUpRead(); + + BlockSpiller spiller = new S3BlockSpiller(amazonS3, spillConfig, allocator, response.getSchema(), evaluator); + provider.readWithConstraint(spiller, readRequest, queryStatusChecker); + + validateRead(response.getSchema(), blockSpillReader, spiller.getSpillLocations(), spillConfig.getEncryptionKey()); + } + + protected void validateRead(Schema schema, S3BlockSpillReader reader, List locations, EncryptionKey encryptionKey) + { + int blockNum = 0; + int rowNum = 0; + for (SpillLocation next : locations) { + S3SpillLocation spillLocation = (S3SpillLocation) next; + try (Block block = reader.read(spillLocation, encryptionKey, schema)) { + logger.info("validateRead: blockNum[{}] and recordCount[{}]", blockNum++, block.getRowCount()); + + for (int i = 0; i < block.getRowCount(); i++) { + logger.info("validateRead: {}", BlockUtils.rowToString(block, i)); + rowNum++; + validateRow(block, i); + } + } + catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + assertEquals(getExpectedRows(), rowNum); + } + + private class ByteHolder + { + private byte[] bytes; + + public void setBytes(byte[] bytes) + { + this.bytes = bytes; + } + + public byte[] getBytes() + { + return bytes; + } + } +} diff --git a/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/EmrClusterTableProviderTest.java b/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/EmrClusterTableProviderTest.java new file mode 100644 index 0000000000..b7d3d75b98 --- /dev/null +++ b/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/EmrClusterTableProviderTest.java @@ -0,0 +1,201 @@ +/*- + * #%L + * athena-aws-cmdb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.aws.cmdb.tables; + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.services.elasticmapreduce.AmazonElasticMapReduce; +import com.amazonaws.services.elasticmapreduce.model.Application; +import com.amazonaws.services.elasticmapreduce.model.Cluster; +import com.amazonaws.services.elasticmapreduce.model.ClusterStateChangeReason; +import com.amazonaws.services.elasticmapreduce.model.ClusterStatus; +import com.amazonaws.services.elasticmapreduce.model.ClusterSummary; +import com.amazonaws.services.elasticmapreduce.model.DescribeClusterRequest; +import com.amazonaws.services.elasticmapreduce.model.DescribeClusterResult; +import com.amazonaws.services.elasticmapreduce.model.ListClustersRequest; +import com.amazonaws.services.elasticmapreduce.model.ListClustersResult; +import com.amazonaws.services.elasticmapreduce.model.Tag; +import org.apache.arrow.vector.complex.reader.FieldReader; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Field; +import org.joda.time.DateTimeZone; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.runners.MockitoJUnitRunner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class EmrClusterTableProviderTest + extends AbstractTableProviderTest +{ + private static final Logger logger = LoggerFactory.getLogger(EmrClusterTableProviderTest.class); + + @Mock + private AmazonElasticMapReduce mockEmr; + + protected String getIdField() + { + return "id"; + } + + protected String getIdValue() + { + return "123"; + } + + protected String getExpectedSchema() + { + return "emr"; + } + + protected String getExpectedTable() + { + return "emr_clusters"; + } + + protected int getExpectedRows() + { + return 2; + } + + protected TableProvider setUpSource() + { + return new EmrClusterTableProvider(mockEmr); + } + + @Override + protected void setUpRead() + { + when(mockEmr.listClusters(any(ListClustersRequest.class))) + .thenAnswer((InvocationOnMock invocation) -> { + ListClustersResult mockResult = mock(ListClustersResult.class); + List values = new ArrayList<>(); + values.add(makeClusterSummary(getIdValue())); + values.add(makeClusterSummary(getIdValue())); + values.add(makeClusterSummary("fake-id")); + when(mockResult.getClusters()).thenReturn(values); + return mockResult; + }); + + when(mockEmr.describeCluster(any(DescribeClusterRequest.class))) + .thenAnswer((InvocationOnMock invocation) -> { + DescribeClusterRequest request = (DescribeClusterRequest) invocation.getArguments()[0]; + DescribeClusterResult mockResult = mock(DescribeClusterResult.class); + List values = new ArrayList<>(); + values.add(makeClusterSummary(getIdValue())); + values.add(makeClusterSummary(getIdValue())); + values.add(makeClusterSummary("fake-id")); + when(mockResult.getCluster()).thenReturn(makeCluster(request.getClusterId())); + return mockResult; + }); + } + + protected void validateRow(Block block, int pos) + { + for (FieldReader fieldReader : block.getFieldReaders()) { + fieldReader.setPosition(pos); + Field field = fieldReader.getField(); + + if (field.getName().equals(getIdField())) { + assertEquals(getIdValue(), fieldReader.readText().toString()); + } + else { + validate(fieldReader); + } + } + } + + private void validate(FieldReader fieldReader) + { + Field field = fieldReader.getField(); + Types.MinorType type = Types.getMinorTypeForArrowType(field.getType()); + switch (type) { + case VARCHAR: + if (field.getName().equals("$data$") || field.getName().equals("direction")) { + assertNotNull(fieldReader.readText().toString()); + } + else { + assertEquals(field.getName(), fieldReader.readText().toString()); + } + break; + case DATEMILLI: + assertEquals(100_000, fieldReader.readLocalDateTime().toDateTime(DateTimeZone.UTC).getMillis()); + break; + case BIT: + assertTrue(fieldReader.readBoolean()); + break; + case INT: + assertTrue(fieldReader.readInteger() > 0); + break; + case STRUCT: + for (Field child : field.getChildren()) { + validate(fieldReader.reader(child.getName())); + } + break; + case LIST: + validate(fieldReader.reader()); + break; + default: + throw new RuntimeException("No validation configured for field " + field.getName() + ":" + type + " " + field.getChildren()); + } + } + + private ClusterSummary makeClusterSummary(String id) + { + return new ClusterSummary() + .withName("name") + .withId(id) + .withStatus(new ClusterStatus() + .withState("state") + .withStateChangeReason(new ClusterStateChangeReason() + .withCode("state_code") + .withMessage("state_msg"))) + .withNormalizedInstanceHours(100); + } + + private Cluster makeCluster(String id) + { + return new Cluster() + .withId(id) + .withName("name") + .withAutoScalingRole("autoscaling_role") + .withCustomAmiId("custom_ami") + .withInstanceCollectionType("instance_collection_type") + .withLogUri("log_uri") + .withMasterPublicDnsName("master_public_dns") + .withReleaseLabel("release_label") + .withRunningAmiVersion("running_ami") + .withScaleDownBehavior("scale_down_behavior") + .withServiceRole("service_role") + .withApplications(new Application().withName("name").withVersion("version")) + .withTags(new Tag("key", "value")); + } +} diff --git a/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/RdsTableProviderTest.java b/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/RdsTableProviderTest.java new file mode 100644 index 0000000000..f27dec682f --- /dev/null +++ b/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/RdsTableProviderTest.java @@ -0,0 +1,237 @@ +/*- + * #%L + * athena-aws-cmdb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.aws.cmdb.tables; + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.services.elasticmapreduce.AmazonElasticMapReduce; +import com.amazonaws.services.elasticmapreduce.model.Application; +import com.amazonaws.services.elasticmapreduce.model.Cluster; +import com.amazonaws.services.elasticmapreduce.model.ClusterStateChangeReason; +import com.amazonaws.services.elasticmapreduce.model.ClusterStatus; +import com.amazonaws.services.elasticmapreduce.model.ClusterSummary; +import com.amazonaws.services.elasticmapreduce.model.DescribeClusterRequest; +import com.amazonaws.services.elasticmapreduce.model.DescribeClusterResult; +import com.amazonaws.services.elasticmapreduce.model.ListClustersRequest; +import com.amazonaws.services.elasticmapreduce.model.ListClustersResult; +import com.amazonaws.services.elasticmapreduce.model.Tag; +import com.amazonaws.services.rds.AmazonRDS; +import com.amazonaws.services.rds.model.DBInstance; +import com.amazonaws.services.rds.model.DBInstanceStatusInfo; +import com.amazonaws.services.rds.model.DBParameterGroup; +import com.amazonaws.services.rds.model.DBParameterGroupStatus; +import com.amazonaws.services.rds.model.DBSecurityGroupMembership; +import com.amazonaws.services.rds.model.DBSubnetGroup; +import com.amazonaws.services.rds.model.DescribeDBInstancesRequest; +import com.amazonaws.services.rds.model.DescribeDBInstancesResult; +import com.amazonaws.services.rds.model.DomainMembership; +import com.amazonaws.services.rds.model.Endpoint; +import com.amazonaws.services.rds.model.Subnet; +import org.apache.arrow.vector.complex.reader.FieldReader; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Field; +import org.joda.time.DateTimeZone; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.runners.MockitoJUnitRunner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.concurrent.atomic.AtomicLong; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class RdsTableProviderTest + extends AbstractTableProviderTest +{ + private static final Logger logger = LoggerFactory.getLogger(RdsTableProviderTest.class); + + @Mock + private AmazonRDS mockRds; + + protected String getIdField() + { + return "instance_id"; + } + + protected String getIdValue() + { + return "123"; + } + + protected String getExpectedSchema() + { + return "rds"; + } + + protected String getExpectedTable() + { + return "rds_instances"; + } + + protected int getExpectedRows() + { + return 6; + } + + protected TableProvider setUpSource() + { + return new RdsTableProvider(mockRds); + } + + @Override + protected void setUpRead() + { + final AtomicLong requestCount = new AtomicLong(0); + when(mockRds.describeDBInstances(any(DescribeDBInstancesRequest.class))) + .thenAnswer((InvocationOnMock invocation) -> { + DescribeDBInstancesResult mockResult = mock(DescribeDBInstancesResult.class); + List values = new ArrayList<>(); + values.add(makeValue(getIdValue())); + values.add(makeValue(getIdValue())); + values.add(makeValue("fake-id")); + when(mockResult.getDBInstances()).thenReturn(values); + + if (requestCount.incrementAndGet() < 3) { + when(mockResult.getMarker()).thenReturn(String.valueOf(requestCount.get())); + } + return mockResult; + }); + } + + protected void validateRow(Block block, int pos) + { + for (FieldReader fieldReader : block.getFieldReaders()) { + fieldReader.setPosition(pos); + Field field = fieldReader.getField(); + + if (field.getName().equals(getIdField())) { + assertEquals(getIdValue(), fieldReader.readText().toString()); + } + else { + validate(fieldReader); + } + } + } + + private void validate(FieldReader fieldReader) + { + try { + logger.info("validate: {} {}", fieldReader.getField().getName(), fieldReader.getMinorType()); + Field field = fieldReader.getField(); + Types.MinorType type = Types.getMinorTypeForArrowType(field.getType()); + switch (type) { + case VARCHAR: + if (field.getName().equals("$data$")) { + assertNotNull(fieldReader.readText().toString()); + } + else { + assertEquals(field.getName(), fieldReader.readText().toString()); + } + break; + case DATEMILLI: + assertEquals(100_000, fieldReader.readLocalDateTime().toDateTime(DateTimeZone.UTC).getMillis()); + break; + case BIT: + assertTrue(fieldReader.readBoolean()); + break; + case INT: + assertTrue(fieldReader.readInteger() > 0); + break; + case STRUCT: + for (Field child : field.getChildren()) { + validate(fieldReader.reader(child.getName())); + } + break; + case LIST: + validate(fieldReader.reader()); + break; + default: + throw new RuntimeException("No validation configured for field " + field.getName() + ":" + type + " " + field.getChildren()); + } + } + catch (RuntimeException ex) { + throw new RuntimeException("Error validating field " + fieldReader.getField().getName(), ex); + } + } + + private DBInstance makeValue(String id) + { + return new DBInstance() + .withDBInstanceIdentifier(id) + .withAvailabilityZone("primary_az") + .withAllocatedStorage(100) + .withStorageEncrypted(true) + .withBackupRetentionPeriod(100) + .withAutoMinorVersionUpgrade(true) + .withDBInstanceClass("instance_class") + .withDbInstancePort(100) + .withDBInstanceStatus("status") + .withStorageType("storage_type") + .withDbiResourceId("dbi_resource_id") + .withDBName("name") + .withDomainMemberships(new DomainMembership() + .withDomain("domain") + .withFQDN("fqdn") + .withIAMRoleName("iam_role") + .withStatus("status")) + .withEngine("engine") + .withEngineVersion("engine_version") + .withLicenseModel("license_model") + .withSecondaryAvailabilityZone("secondary_az") + .withPreferredBackupWindow("backup_window") + .withPreferredMaintenanceWindow("maint_window") + .withReadReplicaSourceDBInstanceIdentifier("read_replica_source_id") + .withDBParameterGroups(new DBParameterGroupStatus() + .withDBParameterGroupName("name") + .withParameterApplyStatus("status")) + .withDBSecurityGroups(new DBSecurityGroupMembership() + .withDBSecurityGroupName("name") + .withStatus("status")) + .withDBSubnetGroup(new DBSubnetGroup() + .withDBSubnetGroupName("name") + .withSubnetGroupStatus("status") + .withVpcId("vpc") + .withSubnets(new Subnet() + .withSubnetIdentifier("subnet"))) + .withStatusInfos(new DBInstanceStatusInfo() + .withStatus("status") + .withMessage("message") + .withNormal(true) + .withStatusType("type")) + .withEndpoint(new Endpoint() + .withAddress("address") + .withPort(100) + .withHostedZoneId("zone")) + .withInstanceCreateTime(new Date(100000)) + .withIops(100) + .withMultiAZ(true) + .withPubliclyAccessible(true); + } +} diff --git a/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/EbsTableProviderTest.java b/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/EbsTableProviderTest.java new file mode 100644 index 0000000000..cf30fd58eb --- /dev/null +++ b/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/EbsTableProviderTest.java @@ -0,0 +1,181 @@ +/*- + * #%L + * athena-aws-cmdb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.aws.cmdb.tables.ec2; + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connectors.aws.cmdb.tables.AbstractTableProviderTest; +import com.amazonaws.athena.connectors.aws.cmdb.tables.TableProvider; +import com.amazonaws.services.ec2.AmazonEC2; +import com.amazonaws.services.ec2.model.DescribeVolumesRequest; +import com.amazonaws.services.ec2.model.DescribeVolumesResult; +import com.amazonaws.services.ec2.model.Tag; +import com.amazonaws.services.ec2.model.Volume; +import com.amazonaws.services.ec2.model.VolumeAttachment; +import org.apache.arrow.vector.complex.reader.FieldReader; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Field; +import org.joda.time.DateTimeZone; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.runners.MockitoJUnitRunner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class EbsTableProviderTest + extends AbstractTableProviderTest +{ + private static final Logger logger = LoggerFactory.getLogger(EbsTableProviderTest.class); + + @Mock + private AmazonEC2 mockEc2; + + protected String getIdField() + { + return "id"; + } + + protected String getIdValue() + { + return "123"; + } + + protected String getExpectedSchema() + { + return "ec2"; + } + + protected String getExpectedTable() + { + return "ebs_volumes"; + } + + protected int getExpectedRows() + { + return 2; + } + + protected TableProvider setUpSource() + { + return new EbsTableProvider(mockEc2); + } + + @Override + protected void setUpRead() + { + when(mockEc2.describeVolumes(any(DescribeVolumesRequest.class))).thenAnswer((InvocationOnMock invocation) -> { + DescribeVolumesRequest request = (DescribeVolumesRequest) invocation.getArguments()[0]; + + assertEquals(getIdValue(), request.getVolumeIds().get(0)); + DescribeVolumesResult mockResult = mock(DescribeVolumesResult.class); + List values = new ArrayList<>(); + values.add(makeVolume(getIdValue())); + values.add(makeVolume(getIdValue())); + values.add(makeVolume("fake-id")); + when(mockResult.getVolumes()).thenReturn(values); + return mockResult; + }); + } + + protected void validateRow(Block block, int pos) + { + for (FieldReader fieldReader : block.getFieldReaders()) { + fieldReader.setPosition(pos); + Field field = fieldReader.getField(); + + if (field.getName().equals(getIdField())) { + assertEquals(getIdValue(), fieldReader.readText().toString()); + } + else { + validate(fieldReader); + } + } + } + + private void validate(FieldReader fieldReader) + { + Field field = fieldReader.getField(); + Types.MinorType type = Types.getMinorTypeForArrowType(field.getType()); + switch (type) { + case VARCHAR: + if (field.getName().equals("$data$")) { + assertNotNull(fieldReader.readText().toString()); + } + else { + assertEquals(field.getName(), fieldReader.readText().toString()); + } + break; + case DATEMILLI: + assertEquals(100_000, fieldReader.readLocalDateTime().toDateTime(DateTimeZone.UTC).getMillis()); + break; + case BIT: + assertTrue(fieldReader.readBoolean()); + break; + case INT: + assertTrue(fieldReader.readInteger() > 0); + break; + case STRUCT: + for (Field child : field.getChildren()) { + validate(fieldReader.reader(child.getName())); + } + break; + case LIST: + validate(fieldReader.reader()); + break; + default: + throw new RuntimeException("No validation configured for field " + field.getName() + ":" + type + " " + field.getChildren()); + } + } + + private Volume makeVolume(String id) + { + Volume volume = new Volume(); + volume.withVolumeId(id) + .withVolumeType("type") + .withAttachments(new VolumeAttachment() + .withInstanceId("target") + .withDevice("attached_device") + .withState("attachment_state") + .withAttachTime(new Date(100_000))) + .withAvailabilityZone("availability_zone") + .withCreateTime(new Date(100_000)) + .withEncrypted(true) + .withKmsKeyId("kms_key_id") + .withSize(100) + .withIops(100) + .withSnapshotId("snapshot_id") + .withState("state") + .withTags(new Tag("key", "value")); + + return volume; + } +} diff --git a/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/Ec2TableProviderTest.java b/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/Ec2TableProviderTest.java new file mode 100644 index 0000000000..478d81100c --- /dev/null +++ b/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/Ec2TableProviderTest.java @@ -0,0 +1,226 @@ +/*- + * #%L + * athena-aws-cmdb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.aws.cmdb.tables.ec2; + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connectors.aws.cmdb.tables.AbstractTableProviderTest; +import com.amazonaws.athena.connectors.aws.cmdb.tables.TableProvider; +import com.amazonaws.services.ec2.AmazonEC2; +import com.amazonaws.services.ec2.model.DescribeInstancesRequest; +import com.amazonaws.services.ec2.model.DescribeInstancesResult; +import com.amazonaws.services.ec2.model.EbsInstanceBlockDevice; +import com.amazonaws.services.ec2.model.GroupIdentifier; +import com.amazonaws.services.ec2.model.Instance; +import com.amazonaws.services.ec2.model.InstanceBlockDeviceMapping; +import com.amazonaws.services.ec2.model.InstanceNetworkInterface; +import com.amazonaws.services.ec2.model.InstanceState; +import com.amazonaws.services.ec2.model.Reservation; +import com.amazonaws.services.ec2.model.StateReason; +import org.apache.arrow.vector.complex.reader.FieldReader; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Field; +import org.joda.time.DateTimeZone; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.runners.MockitoJUnitRunner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class Ec2TableProviderTest + extends AbstractTableProviderTest +{ + private static final Logger logger = LoggerFactory.getLogger(Ec2TableProviderTest.class); + + @Mock + private AmazonEC2 mockEc2; + + protected String getIdField() + { + return "instance_id"; + } + + protected String getIdValue() + { + return "123"; + } + + protected String getExpectedSchema() + { + return "ec2"; + } + + protected String getExpectedTable() + { + return "ec2_instances"; + } + + protected int getExpectedRows() + { + return 4; + } + + protected TableProvider setUpSource() + { + return new Ec2TableProvider(mockEc2); + } + + @Override + protected void setUpRead() + { + when(mockEc2.describeInstances(any(DescribeInstancesRequest.class))).thenAnswer((InvocationOnMock invocation) -> { + DescribeInstancesRequest request = (DescribeInstancesRequest) invocation.getArguments()[0]; + + assertEquals(getIdValue(), request.getInstanceIds().get(0)); + DescribeInstancesResult mockResult = mock(DescribeInstancesResult.class); + List reservations = new ArrayList<>(); + reservations.add(makeReservation()); + reservations.add(makeReservation()); + when(mockResult.getReservations()).thenReturn(reservations); + return mockResult; + }); + } + + protected void validateRow(Block block, int pos) + { + for (FieldReader fieldReader : block.getFieldReaders()) { + fieldReader.setPosition(pos); + Field field = fieldReader.getField(); + + if (field.getName().equals(getIdField())) { + assertEquals(getIdValue(), fieldReader.readText().toString()); + } + else { + validate(fieldReader); + } + } + } + + private void validate(FieldReader fieldReader) + { + Field field = fieldReader.getField(); + Types.MinorType type = Types.getMinorTypeForArrowType(field.getType()); + switch (type) { + case VARCHAR: + if (field.getName().equals("$data$")) { + assertNotNull(fieldReader.readText().toString()); + } + else { + assertEquals(field.getName(), fieldReader.readText().toString()); + } + break; + case DATEMILLI: + assertEquals(100_000, fieldReader.readLocalDateTime().toDateTime(DateTimeZone.UTC).getMillis()); + break; + case BIT: + assertTrue(fieldReader.readBoolean()); + break; + case INT: + assertTrue(fieldReader.readInteger() > 0); + break; + case STRUCT: + for (Field child : field.getChildren()) { + validate(fieldReader.reader(child.getName())); + } + break; + case LIST: + validate(fieldReader.reader()); + break; + default: + throw new RuntimeException("No validation configured for field " + field.getName() + ":" + type + " " + field.getChildren()); + } + } + + private Reservation makeReservation() + { + Reservation reservation = mock(Reservation.class); + List instances = new ArrayList<>(); + instances.add(makeInstance(getIdValue())); + instances.add(makeInstance(getIdValue())); + instances.add(makeInstance("non-matching-id")); + when(reservation.getInstances()).thenReturn(instances); + return reservation; + } + + private Instance makeInstance(String id) + { + Instance instance = new Instance(); + instance.withInstanceId(id) + .withImageId("image_id") + .withInstanceType("instance_type") + .withPlatform("platform") + .withPrivateDnsName("private_dns_name") + .withPrivateIpAddress("private_ip_address") + .withPublicDnsName("public_dns_name") + .withPublicIpAddress("public_ip_address") + .withSubnetId("subnet_id") + .withVpcId("vpc_id") + .withArchitecture("architecture") + .withInstanceLifecycle("instance_lifecycle") + .withRootDeviceName("root_device_name") + .withRootDeviceType("root_device_type") + .withSpotInstanceRequestId("spot_instance_request_id") + .withVirtualizationType("virtualization_type") + .withKeyName("key_name") + .withKernelId("kernel_id") + .withCapacityReservationId("capacity_reservation_id") + .withLaunchTime(new Date(100_000)) + .withState(new InstanceState().withCode(100).withName("name")) + .withStateReason(new StateReason().withCode("code").withMessage("message")) + .withEbsOptimized(true); + + List interfaces = new ArrayList<>(); + interfaces.add(new InstanceNetworkInterface() + .withStatus("status") + .withSubnetId("subnet") + .withVpcId("vpc") + .withMacAddress("mac_address") + .withPrivateDnsName("private_dns") + .withPrivateIpAddress("private_ip") + .withNetworkInterfaceId("interface_id") + .withGroups(new GroupIdentifier().withGroupId("group_id").withGroupName("group_name"))); + + interfaces.add(new InstanceNetworkInterface() + .withStatus("status") + .withSubnetId("subnet") + .withVpcId("vpc") + .withMacAddress("mac") + .withPrivateDnsName("private_dns") + .withPrivateIpAddress("private_ip") + .withNetworkInterfaceId("interface_id") + .withGroups(new GroupIdentifier().withGroupId("group_id").withGroupName("group_name"))); + + instance.withNetworkInterfaces(interfaces) + .withSecurityGroups(new GroupIdentifier().withGroupId("group_id").withGroupName("group_name")) + .withBlockDeviceMappings(new InstanceBlockDeviceMapping().withDeviceName("device_name").withEbs(new EbsInstanceBlockDevice().withVolumeId("volume_id"))); + + return instance; + } +} diff --git a/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/ImagesTableProviderTest.java b/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/ImagesTableProviderTest.java new file mode 100644 index 0000000000..e58c6ee452 --- /dev/null +++ b/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/ImagesTableProviderTest.java @@ -0,0 +1,193 @@ +/*- + * #%L + * athena-aws-cmdb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.aws.cmdb.tables.ec2; + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connectors.aws.cmdb.tables.AbstractTableProviderTest; +import com.amazonaws.athena.connectors.aws.cmdb.tables.TableProvider; +import com.amazonaws.services.ec2.AmazonEC2; +import com.amazonaws.services.ec2.model.BlockDeviceMapping; +import com.amazonaws.services.ec2.model.DescribeImagesRequest; +import com.amazonaws.services.ec2.model.DescribeImagesResult; +import com.amazonaws.services.ec2.model.EbsBlockDevice; +import com.amazonaws.services.ec2.model.Image; +import com.amazonaws.services.ec2.model.Tag; +import org.apache.arrow.vector.complex.reader.FieldReader; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Field; +import org.joda.time.DateTimeZone; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.runners.MockitoJUnitRunner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class ImagesTableProviderTest + extends AbstractTableProviderTest +{ + private static final Logger logger = LoggerFactory.getLogger(ImagesTableProviderTest.class); + + @Mock + private AmazonEC2 mockEc2; + + protected String getIdField() + { + return "id"; + } + + protected String getIdValue() + { + return "123"; + } + + protected String getExpectedSchema() + { + return "ec2"; + } + + protected String getExpectedTable() + { + return "ec2_images"; + } + + protected int getExpectedRows() + { + return 2; + } + + protected TableProvider setUpSource() + { + return new ImagesTableProvider(mockEc2); + } + + @Override + protected void setUpRead() + { + when(mockEc2.describeImages(any(DescribeImagesRequest.class))).thenAnswer((InvocationOnMock invocation) -> { + DescribeImagesRequest request = (DescribeImagesRequest) invocation.getArguments()[0]; + + assertEquals(getIdValue(), request.getImageIds().get(0)); + DescribeImagesResult mockResult = mock(DescribeImagesResult.class); + List values = new ArrayList<>(); + values.add(makeImage(getIdValue())); + values.add(makeImage(getIdValue())); + values.add(makeImage("fake-id")); + when(mockResult.getImages()).thenReturn(values); + return mockResult; + }); + } + + protected void validateRow(Block block, int pos) + { + for (FieldReader fieldReader : block.getFieldReaders()) { + fieldReader.setPosition(pos); + Field field = fieldReader.getField(); + + if (field.getName().equals(getIdField())) { + assertEquals(getIdValue(), fieldReader.readText().toString()); + } + else { + validate(fieldReader); + } + } + } + + private void validate(FieldReader fieldReader) + { + Field field = fieldReader.getField(); + Types.MinorType type = Types.getMinorTypeForArrowType(field.getType()); + switch (type) { + case VARCHAR: + if (field.getName().equals("$data$")) { + assertNotNull(fieldReader.readText().toString()); + } + else { + assertEquals(field.getName(), fieldReader.readText().toString()); + } + break; + case DATEMILLI: + assertEquals(100_000, fieldReader.readLocalDateTime().toDateTime(DateTimeZone.UTC).getMillis()); + break; + case BIT: + assertTrue(fieldReader.readBoolean()); + break; + case INT: + assertTrue(fieldReader.readInteger() > 0); + break; + case STRUCT: + for (Field child : field.getChildren()) { + validate(fieldReader.reader(child.getName())); + } + break; + case LIST: + validate(fieldReader.reader()); + break; + default: + throw new RuntimeException("No validation configured for field " + field.getName() + ":" + type + " " + field.getChildren()); + } + } + + private Image makeImage(String id) + { + Image image = new Image(); + image.withImageId(id) + .withArchitecture("architecture") + .withCreationDate("created") + .withDescription("description") + .withHypervisor("hypervisor") + .withImageLocation("location") + .withImageType("type") + .withKernelId("kernel") + .withName("name") + .withOwnerId("owner") + .withPlatform("platform") + .withRamdiskId("ramdisk") + .withRootDeviceName("root_device") + .withRootDeviceType("root_type") + .withSriovNetSupport("srvio_net") + .withState("state") + .withVirtualizationType("virt_type") + .withPublic(true) + .withTags(new Tag("key", "value")) + .withBlockDeviceMappings(new BlockDeviceMapping() + .withDeviceName("dev_name") + .withNoDevice("no_device") + .withVirtualName("virt_name") + .withEbs(new EbsBlockDevice() + .withIops(100) + .withKmsKeyId("ebs_kms_key") + .withVolumeType("ebs_type") + .withVolumeSize(100))); + + return image; + } +} diff --git a/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/RouteTableProviderTest.java b/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/RouteTableProviderTest.java new file mode 100644 index 0000000000..f7afdbec72 --- /dev/null +++ b/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/RouteTableProviderTest.java @@ -0,0 +1,187 @@ +/*- + * #%L + * athena-aws-cmdb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.aws.cmdb.tables.ec2; + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connectors.aws.cmdb.tables.AbstractTableProviderTest; +import com.amazonaws.athena.connectors.aws.cmdb.tables.TableProvider; +import com.amazonaws.services.ec2.AmazonEC2; +import com.amazonaws.services.ec2.model.DescribeRouteTablesRequest; +import com.amazonaws.services.ec2.model.DescribeRouteTablesResult; +import com.amazonaws.services.ec2.model.PropagatingVgw; +import com.amazonaws.services.ec2.model.Route; +import com.amazonaws.services.ec2.model.RouteTable; +import com.amazonaws.services.ec2.model.RouteTableAssociation; +import com.amazonaws.services.ec2.model.Tag; +import org.apache.arrow.vector.complex.reader.FieldReader; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Field; +import org.joda.time.DateTimeZone; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.runners.MockitoJUnitRunner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class RouteTableProviderTest + extends AbstractTableProviderTest +{ + private static final Logger logger = LoggerFactory.getLogger(RouteTableProviderTest.class); + + @Mock + private AmazonEC2 mockEc2; + + protected String getIdField() + { + return "route_table_id"; + } + + protected String getIdValue() + { + return "123"; + } + + protected String getExpectedSchema() + { + return "ec2"; + } + + protected String getExpectedTable() + { + return "routing_tables"; + } + + protected int getExpectedRows() + { + return 2; + } + + protected TableProvider setUpSource() + { + return new RouteTableProvider(mockEc2); + } + + @Override + protected void setUpRead() + { + when(mockEc2.describeRouteTables(any(DescribeRouteTablesRequest.class))).thenAnswer((InvocationOnMock invocation) -> { + DescribeRouteTablesRequest request = (DescribeRouteTablesRequest) invocation.getArguments()[0]; + + assertEquals(getIdValue(), request.getRouteTableIds().get(0)); + DescribeRouteTablesResult mockResult = mock(DescribeRouteTablesResult.class); + List values = new ArrayList<>(); + values.add(makeRouteTable(getIdValue())); + values.add(makeRouteTable(getIdValue())); + values.add(makeRouteTable("fake-id")); + when(mockResult.getRouteTables()).thenReturn(values); + return mockResult; + }); + } + + protected void validateRow(Block block, int pos) + { + for (FieldReader fieldReader : block.getFieldReaders()) { + fieldReader.setPosition(pos); + Field field = fieldReader.getField(); + + if (field.getName().equals(getIdField())) { + assertEquals(getIdValue(), fieldReader.readText().toString()); + } + else { + validate(fieldReader); + } + } + } + + private void validate(FieldReader fieldReader) + { + Field field = fieldReader.getField(); + Types.MinorType type = Types.getMinorTypeForArrowType(field.getType()); + switch (type) { + case VARCHAR: + if (field.getName().equals("$data$")) { + assertNotNull(fieldReader.readText().toString()); + } + else { + assertEquals(field.getName(), fieldReader.readText().toString()); + } + break; + case DATEMILLI: + assertEquals(100_000, fieldReader.readLocalDateTime().toDateTime(DateTimeZone.UTC).getMillis()); + break; + case BIT: + assertTrue(fieldReader.readBoolean()); + break; + case INT: + assertTrue(fieldReader.readInteger() > 0); + break; + case STRUCT: + for (Field child : field.getChildren()) { + validate(fieldReader.reader(child.getName())); + } + break; + case LIST: + validate(fieldReader.reader()); + break; + default: + throw new RuntimeException("No validation configured for field " + field.getName() + ":" + type + " " + field.getChildren()); + } + } + + private RouteTable makeRouteTable(String id) + { + RouteTable routeTable = new RouteTable(); + routeTable.withRouteTableId(id) + .withOwnerId("owner") + .withVpcId("vpc") + .withAssociations(new RouteTableAssociation().withSubnetId("subnet").withRouteTableId("route_table_id")) + .withTags(new Tag("key", "value")) + .withPropagatingVgws(new PropagatingVgw().withGatewayId("gateway_id")) + .withRoutes(new Route() + .withDestinationCidrBlock("dst_cidr") + .withDestinationIpv6CidrBlock("dst_cidr_v6") + .withDestinationPrefixListId("dst_prefix_list") + .withEgressOnlyInternetGatewayId("egress_igw") + .withGatewayId("gateway") + .withInstanceId("instance_id") + .withInstanceOwnerId("instance_owner") + .withNatGatewayId("nat_gateway") + .withNetworkInterfaceId("interface") + .withOrigin("origin") + .withState("state") + .withTransitGatewayId("transit_gateway") + .withVpcPeeringConnectionId("vpc_peering_con") + ); + + return routeTable; + } +} diff --git a/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/SecurityGroupsTableProviderTest.java b/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/SecurityGroupsTableProviderTest.java new file mode 100644 index 0000000000..d49562e47d --- /dev/null +++ b/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/SecurityGroupsTableProviderTest.java @@ -0,0 +1,179 @@ +/*- + * #%L + * athena-aws-cmdb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.aws.cmdb.tables.ec2; + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connectors.aws.cmdb.tables.AbstractTableProviderTest; +import com.amazonaws.athena.connectors.aws.cmdb.tables.TableProvider; +import com.amazonaws.services.ec2.AmazonEC2; +import com.amazonaws.services.ec2.model.DescribeSecurityGroupsRequest; +import com.amazonaws.services.ec2.model.DescribeSecurityGroupsResult; +import com.amazonaws.services.ec2.model.IpPermission; +import com.amazonaws.services.ec2.model.IpRange; +import com.amazonaws.services.ec2.model.Ipv6Range; +import com.amazonaws.services.ec2.model.PrefixListId; +import com.amazonaws.services.ec2.model.SecurityGroup; +import com.amazonaws.services.ec2.model.UserIdGroupPair; +import org.apache.arrow.vector.complex.reader.FieldReader; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Field; +import org.joda.time.DateTimeZone; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.runners.MockitoJUnitRunner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class SecurityGroupsTableProviderTest + extends AbstractTableProviderTest +{ + private static final Logger logger = LoggerFactory.getLogger(SecurityGroupsTableProviderTest.class); + + @Mock + private AmazonEC2 mockEc2; + + protected String getIdField() + { + return "id"; + } + + protected String getIdValue() + { + return "123"; + } + + protected String getExpectedSchema() + { + return "ec2"; + } + + protected String getExpectedTable() + { + return "security_groups"; + } + + protected int getExpectedRows() + { + return 2; + } + + protected TableProvider setUpSource() + { + return new SecurityGroupsTableProvider(mockEc2); + } + + @Override + protected void setUpRead() + { + when(mockEc2.describeSecurityGroups(any(DescribeSecurityGroupsRequest.class))) + .thenAnswer((InvocationOnMock invocation) -> { + DescribeSecurityGroupsRequest request = (DescribeSecurityGroupsRequest) invocation.getArguments()[0]; + + assertEquals(getIdValue(), request.getGroupIds().get(0)); + DescribeSecurityGroupsResult mockResult = mock(DescribeSecurityGroupsResult.class); + List values = new ArrayList<>(); + values.add(makeSecurityGroup(getIdValue())); + values.add(makeSecurityGroup(getIdValue())); + values.add(makeSecurityGroup("fake-id")); + when(mockResult.getSecurityGroups()).thenReturn(values); + return mockResult; + }); + } + + protected void validateRow(Block block, int pos) + { + for (FieldReader fieldReader : block.getFieldReaders()) { + fieldReader.setPosition(pos); + Field field = fieldReader.getField(); + + if (field.getName().equals(getIdField())) { + assertEquals(getIdValue(), fieldReader.readText().toString()); + } + else { + validate(fieldReader); + } + } + } + + private void validate(FieldReader fieldReader) + { + Field field = fieldReader.getField(); + Types.MinorType type = Types.getMinorTypeForArrowType(field.getType()); + switch (type) { + case VARCHAR: + if (field.getName().equals("$data$") || field.getName().equals("direction")) { + assertNotNull(fieldReader.readText().toString()); + } + else { + assertEquals(field.getName(), fieldReader.readText().toString()); + } + break; + case DATEMILLI: + assertEquals(100_000, fieldReader.readLocalDateTime().toDateTime(DateTimeZone.UTC).getMillis()); + break; + case BIT: + assertTrue(fieldReader.readBoolean()); + break; + case INT: + assertTrue(fieldReader.readInteger() > 0); + break; + case STRUCT: + for (Field child : field.getChildren()) { + validate(fieldReader.reader(child.getName())); + } + break; + case LIST: + validate(fieldReader.reader()); + break; + default: + throw new RuntimeException("No validation configured for field " + field.getName() + ":" + type + " " + field.getChildren()); + } + } + + private SecurityGroup makeSecurityGroup(String id) + { + return new SecurityGroup() + .withGroupId(id) + .withGroupName("name") + .withDescription("description") + .withIpPermissions(new IpPermission() + .withIpProtocol("protocol") + .withFromPort(100) + .withToPort(100) + .withIpv4Ranges(new IpRange().withCidrIp("cidr").withDescription("description")) + + .withIpv6Ranges(new Ipv6Range().withCidrIpv6("cidr").withDescription("description")) + .withPrefixListIds(new PrefixListId().withPrefixListId("prefix").withDescription("description")) + .withUserIdGroupPairs(new UserIdGroupPair().withGroupId("group_id").withUserId("user_id")) + ); + } +} diff --git a/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/SubnetTableProviderTest.java b/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/SubnetTableProviderTest.java new file mode 100644 index 0000000000..a17e1d3faf --- /dev/null +++ b/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/SubnetTableProviderTest.java @@ -0,0 +1,172 @@ +/*- + * #%L + * athena-aws-cmdb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.aws.cmdb.tables.ec2; + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connectors.aws.cmdb.tables.AbstractTableProviderTest; +import com.amazonaws.athena.connectors.aws.cmdb.tables.TableProvider; +import com.amazonaws.services.ec2.AmazonEC2; +import com.amazonaws.services.ec2.model.DescribeSubnetsRequest; +import com.amazonaws.services.ec2.model.DescribeSubnetsResult; +import com.amazonaws.services.ec2.model.Subnet; +import com.amazonaws.services.ec2.model.Tag; +import org.apache.arrow.vector.complex.reader.FieldReader; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Field; +import org.joda.time.DateTimeZone; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.runners.MockitoJUnitRunner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class SubnetTableProviderTest + extends AbstractTableProviderTest +{ + private static final Logger logger = LoggerFactory.getLogger(SubnetTableProviderTest.class); + + @Mock + private AmazonEC2 mockEc2; + + protected String getIdField() + { + return "id"; + } + + protected String getIdValue() + { + return "123"; + } + + protected String getExpectedSchema() + { + return "ec2"; + } + + protected String getExpectedTable() + { + return "subnets"; + } + + protected int getExpectedRows() + { + return 2; + } + + protected TableProvider setUpSource() + { + return new SubnetTableProvider(mockEc2); + } + + @Override + protected void setUpRead() + { + when(mockEc2.describeSubnets(any(DescribeSubnetsRequest.class))).thenAnswer((InvocationOnMock invocation) -> { + DescribeSubnetsRequest request = (DescribeSubnetsRequest) invocation.getArguments()[0]; + + assertEquals(getIdValue(), request.getSubnetIds().get(0)); + DescribeSubnetsResult mockResult = mock(DescribeSubnetsResult.class); + List values = new ArrayList<>(); + values.add(makeSubnet(getIdValue())); + values.add(makeSubnet(getIdValue())); + values.add(makeSubnet("fake-id")); + when(mockResult.getSubnets()).thenReturn(values); + + return mockResult; + }); + } + + protected void validateRow(Block block, int pos) + { + for (FieldReader fieldReader : block.getFieldReaders()) { + fieldReader.setPosition(pos); + Field field = fieldReader.getField(); + + if (field.getName().equals(getIdField())) { + assertEquals(getIdValue(), fieldReader.readText().toString()); + } + else { + validate(fieldReader); + } + } + } + + private void validate(FieldReader fieldReader) + { + Field field = fieldReader.getField(); + Types.MinorType type = Types.getMinorTypeForArrowType(field.getType()); + switch (type) { + case VARCHAR: + if (field.getName().equals("$data$")) { + assertNotNull(fieldReader.readText().toString()); + } + else { + assertEquals(field.getName(), fieldReader.readText().toString()); + } + break; + case DATEMILLI: + assertEquals(100_000, fieldReader.readLocalDateTime().toDateTime(DateTimeZone.UTC).getMillis()); + break; + case BIT: + assertTrue(fieldReader.readBoolean()); + break; + case INT: + assertTrue(fieldReader.readInteger() > 0); + break; + case STRUCT: + for (Field child : field.getChildren()) { + validate(fieldReader.reader(child.getName())); + } + break; + case LIST: + validate(fieldReader.reader()); + break; + default: + throw new RuntimeException("No validation configured for field " + field.getName() + ":" + type + " " + field.getChildren()); + } + } + + private Subnet makeSubnet(String id) + { + return new Subnet() + .withSubnetId(id) + .withAvailabilityZone("availability_zone") + .withCidrBlock("cidr_block") + .withAvailableIpAddressCount(100) + .withDefaultForAz(true) + .withMapPublicIpOnLaunch(true) + .withOwnerId("owner") + .withState("state") + .withTags(new Tag().withKey("key").withValue("value")) + .withVpcId("vpc"); + } +} diff --git a/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/VpcTableProviderTest.java b/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/VpcTableProviderTest.java new file mode 100644 index 0000000000..f22b9b4d8d --- /dev/null +++ b/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/ec2/VpcTableProviderTest.java @@ -0,0 +1,171 @@ +/*- + * #%L + * athena-aws-cmdb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.aws.cmdb.tables.ec2; + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connectors.aws.cmdb.tables.AbstractTableProviderTest; +import com.amazonaws.athena.connectors.aws.cmdb.tables.TableProvider; +import com.amazonaws.services.ec2.AmazonEC2; +import com.amazonaws.services.ec2.model.DescribeVpcsRequest; +import com.amazonaws.services.ec2.model.DescribeVpcsResult; +import com.amazonaws.services.ec2.model.Tag; +import com.amazonaws.services.ec2.model.Vpc; +import org.apache.arrow.vector.complex.reader.FieldReader; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Field; +import org.joda.time.DateTimeZone; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.runners.MockitoJUnitRunner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class VpcTableProviderTest + extends AbstractTableProviderTest +{ + private static final Logger logger = LoggerFactory.getLogger(VpcTableProviderTest.class); + + @Mock + private AmazonEC2 mockEc2; + + protected String getIdField() + { + return "id"; + } + + protected String getIdValue() + { + return "123"; + } + + protected String getExpectedSchema() + { + return "ec2"; + } + + protected String getExpectedTable() + { + return "vpcs"; + } + + protected int getExpectedRows() + { + return 2; + } + + protected TableProvider setUpSource() + { + return new VpcTableProvider(mockEc2); + } + + @Override + protected void setUpRead() + { + when(mockEc2.describeVpcs(any(DescribeVpcsRequest.class))).thenAnswer((InvocationOnMock invocation) -> { + DescribeVpcsRequest request = (DescribeVpcsRequest) invocation.getArguments()[0]; + + assertEquals(getIdValue(), request.getVpcIds().get(0)); + DescribeVpcsResult mockResult = mock(DescribeVpcsResult.class); + List values = new ArrayList<>(); + values.add(makeVpc(getIdValue())); + values.add(makeVpc(getIdValue())); + values.add(makeVpc("fake-id")); + when(mockResult.getVpcs()).thenReturn(values); + return mockResult; + }); + } + + protected void validateRow(Block block, int pos) + { + for (FieldReader fieldReader : block.getFieldReaders()) { + fieldReader.setPosition(pos); + Field field = fieldReader.getField(); + + if (field.getName().equals(getIdField())) { + assertEquals(getIdValue(), fieldReader.readText().toString()); + } + else { + validate(fieldReader); + } + } + } + + private void validate(FieldReader fieldReader) + { + Field field = fieldReader.getField(); + Types.MinorType type = Types.getMinorTypeForArrowType(field.getType()); + switch (type) { + case VARCHAR: + if (field.getName().equals("$data$")) { + assertNotNull(fieldReader.readText().toString()); + } + else { + assertEquals(field.getName(), fieldReader.readText().toString()); + } + break; + case DATEMILLI: + assertEquals(100_000, fieldReader.readLocalDateTime().toDateTime(DateTimeZone.UTC).getMillis()); + break; + case BIT: + assertTrue(fieldReader.readBoolean()); + break; + case INT: + assertTrue(fieldReader.readInteger() > 0); + break; + case STRUCT: + for (Field child : field.getChildren()) { + validate(fieldReader.reader(child.getName())); + } + break; + case LIST: + validate(fieldReader.reader()); + break; + default: + throw new RuntimeException("No validation configured for field " + field.getName() + ":" + type + " " + field.getChildren()); + } + } + + private Vpc makeVpc(String id) + { + Vpc vpc = new Vpc(); + vpc.withVpcId(id) + .withCidrBlock("cidr_block") + .withDhcpOptionsId("dhcp_opts") + .withInstanceTenancy("tenancy") + .withOwnerId("owner") + .withState("state") + .withIsDefault(true) + .withTags(new Tag("key", "valye")); + + return vpc; + } +} diff --git a/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/s3/S3BucketsTableProviderTest.java b/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/s3/S3BucketsTableProviderTest.java new file mode 100644 index 0000000000..0e57ef3027 --- /dev/null +++ b/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/s3/S3BucketsTableProviderTest.java @@ -0,0 +1,155 @@ +/*- + * #%L + * athena-aws-cmdb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.aws.cmdb.tables.s3; + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connectors.aws.cmdb.tables.AbstractTableProviderTest; +import com.amazonaws.athena.connectors.aws.cmdb.tables.TableProvider; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.Bucket; +import com.amazonaws.services.s3.model.Owner; +import org.apache.arrow.vector.complex.reader.FieldReader; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Field; +import org.joda.time.DateTimeZone; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.when; + +public class S3BucketsTableProviderTest + extends AbstractTableProviderTest +{ + private static final Logger logger = LoggerFactory.getLogger(S3BucketsTableProviderTest.class); + + @Mock + private AmazonS3 mockS3; + + protected String getIdField() + { + return "bucket_name"; + } + + protected String getIdValue() + { + return "my_bucket"; + } + + protected String getExpectedSchema() + { + return "s3"; + } + + protected String getExpectedTable() + { + return "buckets"; + } + + protected int getExpectedRows() + { + return 2; + } + + protected TableProvider setUpSource() + { + return new S3BucketsTableProvider(mockS3); + } + + @Override + protected void setUpRead() + { + when(mockS3.listBuckets()).thenAnswer((InvocationOnMock invocation) -> { + List values = new ArrayList<>(); + values.add(makeBucket(getIdValue())); + values.add(makeBucket(getIdValue())); + values.add(makeBucket("fake-id")); + return values; + }); + } + + protected void validateRow(Block block, int pos) + { + for (FieldReader fieldReader : block.getFieldReaders()) { + fieldReader.setPosition(pos); + Field field = fieldReader.getField(); + + if (field.getName().equals(getIdField())) { + assertEquals(getIdValue(), fieldReader.readText().toString()); + } + else { + validate(fieldReader); + } + } + } + + private void validate(FieldReader fieldReader) + { + Field field = fieldReader.getField(); + Types.MinorType type = Types.getMinorTypeForArrowType(field.getType()); + switch (type) { + case VARCHAR: + if (field.getName().equals("$data$")) { + assertNotNull(fieldReader.readText().toString()); + } + else { + assertEquals(field.getName(), fieldReader.readText().toString()); + } + break; + case DATEMILLI: + assertEquals(100_000, fieldReader.readLocalDateTime().toDateTime(DateTimeZone.UTC).getMillis()); + break; + case BIT: + assertTrue(fieldReader.readBoolean()); + break; + case INT: + assertTrue(fieldReader.readInteger() > 0); + break; + case STRUCT: + for (Field child : field.getChildren()) { + validate(fieldReader.reader(child.getName())); + } + break; + case LIST: + validate(fieldReader.reader()); + break; + default: + throw new RuntimeException("No validation configured for field " + field.getName() + ":" + type + " " + field.getChildren()); + } + } + + private Bucket makeBucket(String id) + { + Bucket bucket = new Bucket(); + bucket.setName(id); + Owner owner = new Owner(); + owner.setDisplayName("owner_name"); + owner.setId("owner_id"); + bucket.setOwner(owner); + bucket.setCreationDate(new Date(100_000)); + return bucket; + } +} diff --git a/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/s3/S3ObjectsTableProviderTest.java b/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/s3/S3ObjectsTableProviderTest.java new file mode 100644 index 0000000000..3499e800e9 --- /dev/null +++ b/athena-aws-cmdb/src/test/java/com/amazonaws/athena/connectors/aws/cmdb/tables/s3/S3ObjectsTableProviderTest.java @@ -0,0 +1,185 @@ +/*- + * #%L + * athena-aws-cmdb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.aws.cmdb.tables.s3; + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connectors.aws.cmdb.tables.AbstractTableProviderTest; +import com.amazonaws.athena.connectors.aws.cmdb.tables.TableProvider; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.ListObjectsV2Request; +import com.amazonaws.services.s3.model.ListObjectsV2Result; +import com.amazonaws.services.s3.model.Owner; +import com.amazonaws.services.s3.model.S3ObjectSummary; +import org.apache.arrow.vector.complex.reader.FieldReader; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Field; +import org.joda.time.DateTimeZone; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.concurrent.atomic.AtomicLong; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class S3ObjectsTableProviderTest + extends AbstractTableProviderTest +{ + private static final Logger logger = LoggerFactory.getLogger(S3ObjectsTableProviderTest.class); + + @Mock + private AmazonS3 mockS3; + + protected String getIdField() + { + return "bucket_name"; + } + + protected String getIdValue() + { + return "my_bucket"; + } + + protected String getExpectedSchema() + { + return "s3"; + } + + protected String getExpectedTable() + { + return "objects"; + } + + protected int getExpectedRows() + { + return 4; + } + + protected TableProvider setUpSource() + { + return new S3ObjectsTableProvider(mockS3); + } + + @Override + protected void setUpRead() + { + AtomicLong count = new AtomicLong(0); + when(mockS3.listObjectsV2(any(ListObjectsV2Request.class))).thenAnswer((InvocationOnMock invocation) -> { + ListObjectsV2Request request = (ListObjectsV2Request) invocation.getArguments()[0]; + assertEquals(getIdValue(), request.getBucketName()); + + ListObjectsV2Result mockResult = mock(ListObjectsV2Result.class); + List values = new ArrayList<>(); + values.add(makeObjectSummary(getIdValue())); + values.add(makeObjectSummary(getIdValue())); + values.add(makeObjectSummary("fake-id")); + when(mockResult.getObjectSummaries()).thenReturn(values); + + if (count.get() > 0) { + assertNotNull(request.getContinuationToken()); + } + + if (count.incrementAndGet() < 2) { + when(mockResult.isTruncated()).thenReturn(true); + when(mockResult.getNextContinuationToken()).thenReturn("token"); + } + + return mockResult; + }); + } + + protected void validateRow(Block block, int pos) + { + for (FieldReader fieldReader : block.getFieldReaders()) { + fieldReader.setPosition(pos); + Field field = fieldReader.getField(); + + if (field.getName().equals(getIdField())) { + assertEquals(getIdValue(), fieldReader.readText().toString()); + } + else { + validate(fieldReader); + } + } + } + + private void validate(FieldReader fieldReader) + { + Field field = fieldReader.getField(); + Types.MinorType type = Types.getMinorTypeForArrowType(field.getType()); + switch (type) { + case VARCHAR: + if (field.getName().equals("$data$")) { + assertNotNull(fieldReader.readText().toString()); + } + else { + assertEquals(field.getName(), fieldReader.readText().toString()); + } + break; + case DATEMILLI: + assertEquals(100_000, fieldReader.readLocalDateTime().toDateTime(DateTimeZone.UTC).getMillis()); + break; + case BIT: + assertTrue(fieldReader.readBoolean()); + break; + case INT: + assertTrue(fieldReader.readInteger() > 0); + break; + case BIGINT: + assertTrue(fieldReader.readLong() > 0); + break; + case STRUCT: + for (Field child : field.getChildren()) { + validate(fieldReader.reader(child.getName())); + } + break; + case LIST: + validate(fieldReader.reader()); + break; + default: + throw new RuntimeException("No validation configured for field " + field.getName() + ":" + type + " " + field.getChildren()); + } + } + + private S3ObjectSummary makeObjectSummary(String id) + { + S3ObjectSummary summary = new S3ObjectSummary(); + Owner owner = new Owner(); + owner.setId("owner_id"); + owner.setDisplayName("owner_name"); + summary.setOwner(owner); + summary.setBucketName(id); + summary.setETag("e_tag"); + summary.setKey("key"); + summary.setSize(100); + summary.setLastModified(new Date(100_000)); + summary.setStorageClass("storage_class"); + return summary; + } +} diff --git a/athena-bigquery/LICENSE.txt b/athena-bigquery/LICENSE.txt new file mode 100644 index 0000000000..418de4c108 --- /dev/null +++ b/athena-bigquery/LICENSE.txt @@ -0,0 +1,174 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. \ No newline at end of file diff --git a/athena-bigquery/README.md b/athena-bigquery/README.md new file mode 100644 index 0000000000..90f224dce7 --- /dev/null +++ b/athena-bigquery/README.md @@ -0,0 +1,40 @@ +# Amazon Athena Google BigQuery Connector + +This connector enables Amazon Athena to communicate with BigQuery, making your BigQuery data accessible. + +### Parameters + +The Athena Google BigQuery Connector exposes several configuration options via Lambda environment variables. More detail on the available parameters can be found below. + +|Parameter Name|Example Value|Description| +|--------------|--------------------|------------------| +|spill_bucket|my_bucket|When the data returned by your Lambda function exceeds Lambda’s limits, this is the bucket that the data will be written to for Athena to read the excess from.| +|spill_prefix|temporary/split| (Optional) Defaults to sub-folder in your bucket called 'athena-federation-spill'. Used in conjunction with spill_bucket, this is the path within the above bucket that large responses are spilled to. You should configure an S3 lifecycle on this location to delete old spills after X days/Hours.| +|kms_key_id|a7e63k4b-8loc-40db-a2a1-4d0en2cd8331|(Optional) By default any data that is spilled to S3 is encrypted using AES-GCM and a randomly generated key. Setting a KMS Key ID allows your Lambda function to use KMS for key generation for a stronger source of encryption keys.| +|disable_spill_encryption|True or False|(Optional) Defaults to False so that any data that is spilled to S3 is encrypted using AES-GMC either with a randomly generated key or using KMS to generate keys. Setting this to false will disable spill encryption. You may wish to disable this for improved performance, especially if your spill location in S3 uses S3 Server Side Encryption.| +|gcp_project_id|semiotic-primer-1234567|The project id (not project name) that contains the datasets that this connector should read from.| +|secret_manager_gcp_creds_name|GoogleCloudPlatformCredentials|The name of the secret within AWS Secrets Manager that contains your BigQuery credentials JSON. The credentials | + + ### Deploying The Connector + + To use this connector in your queries, navigate to AWS Serverless Application Repository and deploy a pre-built version of this connector. Alternatively, you can build and deploy this connector from source follow the below steps or use the more detailed tutorial in the athena-example module: + + 1. From the athena-federation-sdk dir, run `mvn clean install` if you haven't already. + 2. From the athena-bigquery dir, run `mvn clean install`. + 3. From the athena-bigquery dir, run `../tools/publish.sh S3_BUCKET_NAME athena-bigquery` to publish the connector to your private AWS Serverless Application Repository. The S3_BUCKET in the command is where a copy of the connector's code will be stored for Serverless Application Repository to retrieve it. This will allow users with permission to do so, the ability to deploy instances of the connector via 1-Click form. Then navigate to [Serverless Application Repository](https://aws.amazon.com/serverless/serverlessrepo) + + +## Limitations and Other Notes + +The following is a list of limitations or other notes. +- Lambda has a maximum timeout value of 15 mins. Each split executes a query on BigQuery and must finish with enough time to store the results for Athena to read. If the Lambda times out, the query will fail. +- Google BigQuery is case sensitive. We attempt to correct the case of dataset names, and table names but we do not do any case correction for project id's. This is necessary because Presto lower cases all metadata. These corrections will make many extra calls to Google BigQuery. +- Many data types are currently not supported, such as Timestamps, Dates, Binary, and Complex data types such as Maps, Lists, Structs. + +## Performance + +This connector will attempt to push as many constraints to Google BigQuery to decrease the number of results returned. This connector currently does not support querying partitioned tables. This will be added in a future release. + +## License + +This project is licensed under the Apache-2.0 License. \ No newline at end of file diff --git a/athena-bigquery/athena-bigquery.yaml b/athena-bigquery/athena-bigquery.yaml new file mode 100644 index 0000000000..5a03b10707 --- /dev/null +++ b/athena-bigquery/athena-bigquery.yaml @@ -0,0 +1,79 @@ +Transform: 'AWS::Serverless-2016-10-31' + +Metadata: + AWS::ServerlessRepo::Application: + Name: AthenaBigQueryConnector + Description: An Athena connector to interact with BigQuery + Author: Amazon Athena + SpdxLicenseId: Apache-2.0 + LicenseUrl: LICENSE.txt + ReadmeUrl: README.md + Labels: ['athena-federation', 'BigQuery'] + HomePageUrl: https://github.com/awslabs/aws-athena-query-federation + SemanticVersion: 1.0.0 + SourceCodeUrl: https://github.com/awslabs/aws-athena-query-federation + +# Parameters are CloudFormation features to pass input +# to your template when you create a stack +Parameters: + AthenaCatalogName: + Description: "The name you will give to this catalog in Athena will be used as the function name prefix." + Type: String + SpillBucket: + Description: "The bucket where this function can spill data." + Type: String + Default: "athena-spill-test" + SpillPrefix: + Description: "The bucket where this function can spill data." + Type: String + Default: "athena-spill" + LambdaTimeout: + Description: "Maximum Lambda invocation runtime in seconds. (min 1 - 900 max)" + Default: 900 + Type: Number + LambdaMemory: + Description: "Lambda memory in MB (min 128 - 3008 max)." + Default: 3008 + Type: Number + DisableSpillEncryption: + Description: "WARNING: If set to 'true' encryption for spilled data is disabled." + Default: "false" + Type: String + GCPProjectID: + Description: "The project ID within Google Cloud Platform ." + Default: BigQueryCred + Type: String + SecretManagerGCPCredsName: + Description: "The secret name within AWS Secrets Manager that contains your Google Cloud Platform Credentials." + Default: GoogleCloudPlatformCredentials + Type: String + +Resources: + ConnectorConfig: + Type: 'AWS::Serverless::Function' + Properties: + Environment: + Variables: + disable_spill_encryption: !Ref DisableSpillEncryption + spill_bucket: !Ref SpillBucket + spill_prefix: !Ref SpillPrefix + secret_manager_big_query_creds_name: !Ref SecretManagerGCPCredsName + gcp_project_id: !Ref GCPProjectID + FunctionName: !Sub "${AthenaCatalogName}" + Handler: "com.amazonaws.athena.connectors.bigquery.BigQueryCompositeHandler" + CodeUri: "./target/athena-bigquery-1.0-SNAPSHOT.jar" + Description: "Allows Athena to call and execute BigQuery queries and process the results." + Runtime: java8 + Timeout: !Ref LambdaTimeout + MemorySize: !Ref LambdaMemory + Policies: + - Statement: + - Action: + - athena:GetQueryExecution + Effect: Allow + Resource: '*' + Version: '2012-10-17' + #S3CrudPolicy allows our connector to spill large responses to S3. You can optionally replace this pre-made policy + #with one that is more restrictive and can only 'put' but not read,delete, or overwrite files. + - S3CrudPolicy: + BucketName: !Ref SpillBucket \ No newline at end of file diff --git a/athena-bigquery/pom.xml b/athena-bigquery/pom.xml new file mode 100644 index 0000000000..5a985025e3 --- /dev/null +++ b/athena-bigquery/pom.xml @@ -0,0 +1,77 @@ + + + + aws-athena-query-federation + com.amazonaws + 1.0 + + 4.0.0 + + athena-bigquery + + + + com.amazonaws + aws-athena-federation-sdk + ${aws-athena-federation-sdk.version} + + + com.google.cloud + google-cloud-bigquery + 1.87.0 + + + com.google.cloud + google-cloud-resourcemanager + 0.108.0-alpha + + + + com.amazonaws + aws-java-sdk-secretsmanager + ${aws-sdk.version} + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.1 + + + package + + shade + + + + + classworlds:classworlds + junit:junit + jmock:* + *:xml-apis + org.apache.maven:lib:tests + + + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + + + + + \ No newline at end of file diff --git a/athena-bigquery/src/main/java/com/amazonaws/athena/connectors/bigquery/BigQueryCompositeHandler.java b/athena-bigquery/src/main/java/com/amazonaws/athena/connectors/bigquery/BigQueryCompositeHandler.java new file mode 100644 index 0000000000..b19b713f8e --- /dev/null +++ b/athena-bigquery/src/main/java/com/amazonaws/athena/connectors/bigquery/BigQueryCompositeHandler.java @@ -0,0 +1,35 @@ +/*- + * #%L + * athena-bigquery + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +package com.amazonaws.athena.connectors.bigquery; + +import com.amazonaws.athena.connector.lambda.handlers.CompositeHandler; + +import java.io.IOException; + +public class BigQueryCompositeHandler + extends CompositeHandler +{ + public BigQueryCompositeHandler() + throws IOException + { + super(new BigQueryMetadataHandler(), new BigQueryRecordHandler()); + } +} diff --git a/athena-bigquery/src/main/java/com/amazonaws/athena/connectors/bigquery/BigQueryConstants.java b/athena-bigquery/src/main/java/com/amazonaws/athena/connectors/bigquery/BigQueryConstants.java new file mode 100644 index 0000000000..ac7688f7fd --- /dev/null +++ b/athena-bigquery/src/main/java/com/amazonaws/athena/connectors/bigquery/BigQueryConstants.java @@ -0,0 +1,46 @@ +/*- + * #%L + * athena-bigquery + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.bigquery; + +public class BigQueryConstants +{ + /** + * The source type that is used to aid in logging diagnostic info when raising a support case. + */ + public static final String SOURCE_TYPE = "bigquery"; + + /** + * The maximum number of datasets and tables that can be returned from Google BigQuery API calls for metadata. + */ + public static final long MAX_RESULTS = 10_000; + + /** + * The Project ID within the Google Cloud Platform where the datasets and tables exist to query. + */ + public static final String GCP_PROJECT_ID = "gcp_project_id"; + + /** + * The name of the secret within Secrets Manager that contains credentials JSON that provides this Lambda access + * to call Google BigQuery. + */ + public static final String ENV_BIG_QUERY_CREDS_SM_ID = "secret_manager_gcp_creds_name"; + + private BigQueryConstants() {} +} diff --git a/athena-bigquery/src/main/java/com/amazonaws/athena/connectors/bigquery/BigQueryExceptions.java b/athena-bigquery/src/main/java/com/amazonaws/athena/connectors/bigquery/BigQueryExceptions.java new file mode 100644 index 0000000000..99a62806d4 --- /dev/null +++ b/athena-bigquery/src/main/java/com/amazonaws/athena/connectors/bigquery/BigQueryExceptions.java @@ -0,0 +1,33 @@ +/*- + * #%L + * athena-bigquery + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +package com.amazonaws.athena.connectors.bigquery; + +public class BigQueryExceptions +{ + static class TooManyTablesException + extends RuntimeException + { + TooManyTablesException() + { + super("Too many tables, exceeded max metadata results for schema count."); + } + } +} diff --git a/athena-bigquery/src/main/java/com/amazonaws/athena/connectors/bigquery/BigQueryMetadataHandler.java b/athena-bigquery/src/main/java/com/amazonaws/athena/connectors/bigquery/BigQueryMetadataHandler.java new file mode 100644 index 0000000000..6c44fd5770 --- /dev/null +++ b/athena-bigquery/src/main/java/com/amazonaws/athena/connectors/bigquery/BigQueryMetadataHandler.java @@ -0,0 +1,179 @@ +/*- + * #%L + * athena-bigquery + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.bigquery; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockWriter; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.spill.SpillLocation; +import com.amazonaws.athena.connector.lambda.handlers.MetadataHandler; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesResponse; +import com.amazonaws.athena.connectors.bigquery.BigQueryExceptions.TooManyTablesException; +import com.google.api.gax.paging.Page; +import com.google.cloud.bigquery.BigQuery; +import com.google.cloud.bigquery.Dataset; +import com.google.cloud.bigquery.DatasetId; +import com.google.cloud.bigquery.Field; +import com.google.cloud.bigquery.Table; +import com.google.cloud.bigquery.TableDefinition; +import com.google.cloud.bigquery.TableId; +import org.apache.arrow.util.VisibleForTesting; +import org.apache.arrow.vector.types.pojo.Schema; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import static com.amazonaws.athena.connectors.bigquery.BigQueryUtils.fixCaseForDatasetName; +import static com.amazonaws.athena.connectors.bigquery.BigQueryUtils.fixCaseForTableName; +import static com.amazonaws.athena.connectors.bigquery.BigQueryUtils.translateToArrowType; + +public class BigQueryMetadataHandler + extends MetadataHandler +{ + private static final Logger logger = LoggerFactory.getLogger(BigQueryMetadataHandler.class); + + /** + * The {@link BigQuery} client to interact with the BigQuery Service. + */ + private final BigQuery bigQuery; + + BigQueryMetadataHandler() + throws IOException + { + this(BigQueryUtils.getBigQueryClient()); + } + + @VisibleForTesting + BigQueryMetadataHandler(BigQuery bigQuery) + { + super(BigQueryConstants.SOURCE_TYPE); + this.bigQuery = bigQuery; + } + + @Override + public ListSchemasResponse doListSchemaNames(BlockAllocator blockAllocator, ListSchemasRequest listSchemasRequest) + { + logger.info("doListSchemaNames called with Catalog: {}", listSchemasRequest.getCatalogName()); + + final List schemas = new ArrayList<>(); + final String projectName = BigQueryUtils.getProjectName(listSchemasRequest); + Page response = bigQuery.listDatasets(projectName); + + for (Dataset dataset : response.iterateAll()) { + if (schemas.size() > BigQueryConstants.MAX_RESULTS) { + throw new TooManyTablesException(); + } + schemas.add(dataset.getDatasetId().getDataset().toLowerCase()); + logger.debug("Found Dataset: {}", dataset.getDatasetId().getDataset()); + } + + logger.info("Found {} schemas!", schemas.size()); + + return new ListSchemasResponse(listSchemasRequest.getCatalogName(), schemas); + } + + @Override + public ListTablesResponse doListTables(BlockAllocator blockAllocator, ListTablesRequest listTablesRequest) + { + logger.info("doListTables called with request {}:{}", listTablesRequest.getCatalogName(), + listTablesRequest.getSchemaName()); + + //Get the project name, dataset name, and dataset id. Google BigQuery is case sensitive. + final String projectName = BigQueryUtils.getProjectName(listTablesRequest); + final String datasetName = fixCaseForDatasetName(projectName, listTablesRequest.getSchemaName(), bigQuery); + final DatasetId datasetId = DatasetId.of(projectName, datasetName); + + Page response = bigQuery.listTables(datasetId); + List tables = new ArrayList<>(); + + for (Table table : response.iterateAll()) { + if (tables.size() > BigQueryConstants.MAX_RESULTS) { + throw new TooManyTablesException(); + } + tables.add(new TableName(listTablesRequest.getSchemaName(), table.getTableId().getTable().toLowerCase())); + } + + logger.info("Found {} table(s)!", tables.size()); + + return new ListTablesResponse(listTablesRequest.getCatalogName(), tables); + } + + @Override + public GetTableResponse doGetTable(BlockAllocator blockAllocator, GetTableRequest getTableRequest) + { + logger.info("doGetTable called with request {}:{}", BigQueryUtils.getProjectName(getTableRequest), + getTableRequest.getTableName()); + + final Schema tableSchema = getSchema(BigQueryUtils.getProjectName(getTableRequest), getTableRequest.getTableName().getSchemaName(), + getTableRequest.getTableName().getTableName()); + return new GetTableResponse(BigQueryUtils.getProjectName(getTableRequest).toLowerCase(), + getTableRequest.getTableName(), tableSchema); + } + + @Override + public void getPartitions(BlockWriter blockWriter, GetTableLayoutRequest request, QueryStatusChecker queryStatusChecker) + throws Exception + { + //NoOp since we don't support partitioning at this time. + } + + @Override + public GetSplitsResponse doGetSplits(BlockAllocator allocator, GetSplitsRequest request) + { + if (logger.isInfoEnabled()) { + logger.info("DoGetSplits: {}.{} Part Cols: {}", request.getSchema(), request.getTableName(), + String.join(",", request.getPartitionCols())); + } + + //Every split must have a unique location if we wish to spill to avoid failures + SpillLocation spillLocation = makeSpillLocation(request); + + return new GetSplitsResponse(request.getCatalogName(), Split.newBuilder(spillLocation, + makeEncryptionKey()).build()); + } + + private Schema getSchema(String projectName, String datasetName, String tableName) + { + datasetName = fixCaseForDatasetName(projectName, datasetName, bigQuery); + tableName = fixCaseForTableName(projectName, datasetName, tableName, bigQuery); + TableId tableId = TableId.of(projectName, datasetName, tableName); + Table response = bigQuery.getTable(tableId); + TableDefinition tableDefinition = response.getDefinition(); + SchemaBuilder schemaBuilder = SchemaBuilder.newBuilder(); + for (Field field : tableDefinition.getSchema().getFields()) { + schemaBuilder.addField(field.getName(), translateToArrowType(field.getType())); + } + return schemaBuilder.build(); + } +} diff --git a/athena-bigquery/src/main/java/com/amazonaws/athena/connectors/bigquery/BigQueryRecordHandler.java b/athena-bigquery/src/main/java/com/amazonaws/athena/connectors/bigquery/BigQueryRecordHandler.java new file mode 100644 index 0000000000..c517f99343 --- /dev/null +++ b/athena-bigquery/src/main/java/com/amazonaws/athena/connectors/bigquery/BigQueryRecordHandler.java @@ -0,0 +1,181 @@ +/*- + * #%L + * athena-bigquery + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +package com.amazonaws.athena.connectors.bigquery; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockSpiller; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.handlers.RecordHandler; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.athena.AmazonAthenaClientBuilder; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.amazonaws.services.secretsmanager.AWSSecretsManagerClientBuilder; +import com.google.cloud.bigquery.BigQuery; +import com.google.cloud.bigquery.BigQueryException; +import com.google.cloud.bigquery.FieldValue; +import com.google.cloud.bigquery.FieldValueList; +import com.google.cloud.bigquery.Job; +import com.google.cloud.bigquery.JobId; +import com.google.cloud.bigquery.JobInfo; +import com.google.cloud.bigquery.QueryJobConfiguration; +import com.google.cloud.bigquery.TableResult; +import com.google.common.annotations.VisibleForTesting; +import org.apache.arrow.vector.types.pojo.Field; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; + +import static com.amazonaws.athena.connectors.bigquery.BigQueryUtils.fixCaseForDatasetName; +import static com.amazonaws.athena.connectors.bigquery.BigQueryUtils.fixCaseForTableName; +import static com.amazonaws.athena.connectors.bigquery.BigQueryUtils.getObjectFromFieldValue; + +/** + * This record handler is an example of how you can implement a lambda that calls bigquery and pulls data. + * This Lambda requires that your BigQuery table is small enough so that a table scan can be completed + * within 5-10 mins or this lambda will time out and it will fail. + */ +public class BigQueryRecordHandler + extends RecordHandler +{ + private static final Logger logger = LoggerFactory.getLogger(BigQueryRecordHandler.class); + + /** + * The {@link BigQuery} client to interact with the BigQuery Service. + */ + private final BigQuery bigQueryClient; + + BigQueryRecordHandler() + throws IOException + { + this(AmazonS3ClientBuilder.defaultClient(), + AWSSecretsManagerClientBuilder.defaultClient(), + AmazonAthenaClientBuilder.defaultClient(), + BigQueryUtils.getBigQueryClient() + ); + } + + @VisibleForTesting + BigQueryRecordHandler(AmazonS3 amazonS3, AWSSecretsManager secretsManager, AmazonAthena athena, BigQuery bigQueryClient) + { + super(amazonS3, secretsManager, athena, BigQueryConstants.SOURCE_TYPE); + this.bigQueryClient = bigQueryClient; + } + + @Override + protected void readWithConstraint(BlockSpiller spiller, ReadRecordsRequest recordsRequest, QueryStatusChecker queryStatusChecker) + throws Exception + { + final String projectName = BigQueryUtils.getProjectName(recordsRequest.getCatalogName()); + final String datasetName = fixCaseForDatasetName(projectName, recordsRequest.getTableName().getSchemaName(), bigQueryClient); + final String tableName = fixCaseForTableName(projectName, datasetName, recordsRequest.getTableName().getTableName(), + bigQueryClient); + + logger.info("Got Request with constraints: {}", recordsRequest.getConstraints()); + + final String sqlToExecute = BigQuerySqlUtils.buildSqlFromSplit(new TableName(datasetName, tableName), + recordsRequest.getSchema(), recordsRequest.getConstraints(), recordsRequest.getSplit()); + + QueryJobConfiguration queryConfig = + QueryJobConfiguration.newBuilder(sqlToExecute) + // Use standard SQL syntax for queries. + // See: https://cloud.google.com/bigquery/sql-reference/ + .setUseLegacySql(false) + .build(); + + logger.info("Executing SQL Query: {} for Split: {}", sqlToExecute, recordsRequest.getSplit()); + + Job queryJob; + try { + JobId jobId = JobId.of(fixRequestId(recordsRequest.getQueryId())); + queryJob = bigQueryClient.create(JobInfo.newBuilder(queryConfig).setJobId(jobId).build()); + } + catch (BigQueryException bqe) { + if (bqe.getMessage().contains("Already Exists: Job")) { + logger.info("Caught exception that this job is already running. "); + //Return silently because another lambda is already processing this. + //Ideally when this happens, we would want to get the existing queryJob. + //This would allow this Lambda to timeout while waiting for the query. + //and rejoin it. This would provide much more time for Lambda to wait for + //BigQuery to finish its query for up to 15 mins * the number of retries. + //However, Presto is creating multiple splits, even if we return a single split. + return; + } + throw bqe; + } + + TableResult result; + try { + while (true) { + if (queryJob.isDone()) { + result = queryJob.getQueryResults(); + break; + } + else if (!queryStatusChecker.isQueryRunning()) { + queryJob.cancel(); + } + else { + Thread.sleep(10); + } + } + } + catch (InterruptedException ie) { + throw new IllegalStateException("Got interrupted waiting for Big Query to finish the query."); + } + + outputResults(spiller, recordsRequest, result); + } + + private String fixRequestId(String queryId) + { + return queryId.replaceAll("[^a-zA-Z0-9-_]", ""); + } + + /** + * Iterates through all the results that comes back from BigQuery and saves the result to be read by the Athena Connector. + * + * @param spiller The {@link BlockSpiller} provided when readWithConstraints() is called. + * @param recordsRequest The {@link ReadRecordsRequest} provided when readWithConstraints() is called. + * @param result The {@link TableResult} provided by {@link BigQuery} client after a query has completed executing. + */ + private void outputResults(BlockSpiller spiller, ReadRecordsRequest recordsRequest, TableResult result) + { + for (FieldValueList row : result.iterateAll()) { + spiller.writeRows((Block block, int rowNum) -> { + boolean isMatched = true; + for (Field field : recordsRequest.getSchema().getFields()) { + FieldValue fieldValue = row.get(field.getName()); + Object val = getObjectFromFieldValue(field.getName(), fieldValue, + field.getFieldType().getType()); + isMatched &= block.offerValue(field.getName(), rowNum, val); + if (!isMatched) { + return 0; + } + } + return 1; + }); + } + } +} diff --git a/athena-bigquery/src/main/java/com/amazonaws/athena/connectors/bigquery/BigQuerySqlUtils.java b/athena-bigquery/src/main/java/com/amazonaws/athena/connectors/bigquery/BigQuerySqlUtils.java new file mode 100644 index 0000000000..d277bdae2f --- /dev/null +++ b/athena-bigquery/src/main/java/com/amazonaws/athena/connectors/bigquery/BigQuerySqlUtils.java @@ -0,0 +1,182 @@ +/*- + * #%L + * athena-bigquery + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +package com.amazonaws.athena.connectors.bigquery; + +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.domain.predicate.EquatableValueSet; +import com.amazonaws.athena.connector.lambda.domain.predicate.Range; +import com.amazonaws.athena.connector.lambda.domain.predicate.SortedRangeSet; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.arrow.vector.types.pojo.Schema; + +import java.util.Map; +import java.util.StringJoiner; + +/** + * Utilities that help with Sql operations. + */ +class BigQuerySqlUtils +{ + private BigQuerySqlUtils() + { + } + + /** + * Builds an SQL statement from the schema, table name, split and contraints that can be executable by + * BigQuery. + * + * @param tableName The table name of the table we are querying. + * @param schema The schema of the table that we are querying. + * @param constraints The constraints that we want to apply to the query. + * @param split The split information to add as a constraint. + * @return SQL Statement that represents the table, columns, split, and constraints. + */ + static String buildSqlFromSplit(TableName tableName, Schema schema, Constraints constraints, Split split) + { + StringBuilder sqlBuilder = new StringBuilder("SELECT "); + + StringJoiner sj = new StringJoiner(","); + if (schema.getFields().isEmpty()) { + sj.add("*"); + } + else { + for (Field field : schema.getFields()) { + sj.add(field.getName()); + } + } + sqlBuilder.append(sj.toString()) + .append(" from ") + .append(tableName.getSchemaName()) + .append(".") + .append(tableName.getTableName()); + + //Buids Where Clause + sj = new StringJoiner(") AND ("); + for (Map.Entry summary : constraints.getSummary().entrySet()) { + final ValueSet value = summary.getValue(); + final String columnName = summary.getKey(); + if (value instanceof EquatableValueSet) { + if (value.isSingleValue()) { + if (value.isNullAllowed()) { + sj.add(columnName + " is null"); + } + else { + //Check Arrow type to see if we + sj.add(columnName + " = " + getValueForWhereClause(columnName, value.getSingleValue(), value.getType())); + } + } + //TODO:: process multiple values in "IN" clause. + } + else if (value instanceof SortedRangeSet) { + SortedRangeSet sortedRangeSet = (SortedRangeSet) value; + if (sortedRangeSet.isNone()) { + if (sortedRangeSet.isNullAllowed()) { + sj.add(columnName + " is null"); + } + //If there is no values and null is not allowed, then that means ignore this valueset. + continue; + } + Range range = sortedRangeSet.getSpan(); + if (!sortedRangeSet.isNullAllowed() && range.getLow().isLowerUnbounded() && range.getHigh().isUpperUnbounded()) { + sj.add(columnName + " is not null"); + continue; + } + if (!range.getLow().isLowerUnbounded() && !range.getLow().isNullValue()) { + final String sqlValue = getValueForWhereClause(columnName, range.getLow().getValue(), value.getType()); + switch (range.getLow().getBound()) { + case ABOVE: + sj.add(columnName + " > " + sqlValue); + break; + case EXACTLY: + sj.add(columnName + " >= " + sqlValue); + break; + case BELOW: + throw new IllegalArgumentException("Low Marker should never use BELOW bound: " + range); + default: + throw new AssertionError("Unhandled bound: " + range.getLow().getBound()); + } + } + if (!range.getHigh().isUpperUnbounded() && !range.getHigh().isNullValue()) { + final String sqlValue = getValueForWhereClause(columnName, range.getHigh().getValue(), value.getType()); + switch (range.getHigh().getBound()) { + case ABOVE: + throw new IllegalArgumentException("High Marker should never use ABOVE bound: " + range); + case EXACTLY: + sj.add(columnName + " <= " + sqlValue); + break; + case BELOW: + sj.add(columnName + " < " + sqlValue); + break; + default: + throw new AssertionError("Unhandled bound: " + range.getHigh().getBound()); + } + } + } + } + if (sj.length() > 0) { + sqlBuilder.append(" WHERE (") + .append(sj.toString()) + .append(")"); + } + + return sqlBuilder.toString(); + } + + //Gets the representation of a value that can be used in a where clause, ie String values need to be quoted, numeric doesn't. + private static String getValueForWhereClause(String columnName, Object value, ArrowType arrowType) + { + switch (arrowType.getTypeID()) { + case Int: + case Decimal: + case FloatingPoint: + return value.toString(); + case Bool: + if ((Boolean) value) { + return "true"; + } + else { + return "false"; + } + case Utf8: + return "'" + value.toString() + "'"; + case Date: + case Time: + case Timestamp: + case Interval: + case Binary: + case FixedSizeBinary: + case Null: + case Struct: + case List: + case FixedSizeList: + case Union: + case NONE: + throw new UnsupportedOperationException("The Arrow type: " + arrowType.getTypeID().name() + " is currently not supported"); + default: + throw new IllegalArgumentException("Unknown type has been encountered during range processing: " + columnName + + " Field Type: " + arrowType.getTypeID().name()); + } + } +} diff --git a/athena-bigquery/src/main/java/com/amazonaws/athena/connectors/bigquery/BigQueryUtils.java b/athena-bigquery/src/main/java/com/amazonaws/athena/connectors/bigquery/BigQueryUtils.java new file mode 100644 index 0000000000..4bc3983a62 --- /dev/null +++ b/athena-bigquery/src/main/java/com/amazonaws/athena/connectors/bigquery/BigQueryUtils.java @@ -0,0 +1,231 @@ +/*- + * #%L + * athena-bigquery + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +package com.amazonaws.athena.connectors.bigquery; + +import com.amazonaws.athena.connector.lambda.metadata.MetadataRequest; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.amazonaws.services.secretsmanager.AWSSecretsManagerClientBuilder; +import com.amazonaws.services.secretsmanager.model.GetSecretValueRequest; +import com.amazonaws.services.secretsmanager.model.GetSecretValueResult; +import com.google.api.gax.paging.Page; +import com.google.auth.Credentials; +import com.google.auth.oauth2.ServiceAccountCredentials; +import com.google.cloud.bigquery.BigQuery; +import com.google.cloud.bigquery.BigQueryOptions; +import com.google.cloud.bigquery.Dataset; +import com.google.cloud.bigquery.DatasetId; +import com.google.cloud.bigquery.FieldValue; +import com.google.cloud.bigquery.LegacySQLTypeName; +import com.google.cloud.bigquery.Table; +import com.google.cloud.resourcemanager.ResourceManager; +import com.google.cloud.resourcemanager.ResourceManagerOptions; +import org.apache.arrow.vector.types.DateUnit; +import org.apache.arrow.vector.types.FloatingPointPrecision; +import org.apache.arrow.vector.types.TimeUnit; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.ArrowType; + +import java.io.ByteArrayInputStream; +import java.io.IOException; + +class BigQueryUtils +{ + private BigQueryUtils() {} + + static Credentials getCredentialsFromSecretsManager() + throws IOException + { + AWSSecretsManager secretsManager = AWSSecretsManagerClientBuilder.defaultClient(); + GetSecretValueResult response = secretsManager.getSecretValue(new GetSecretValueRequest() + .withSecretId(getEnvBigQueryCredsSmId())); + return ServiceAccountCredentials.fromStream(new ByteArrayInputStream(response.getSecretString().getBytes())); + } + + static BigQuery getBigQueryClient() + throws IOException + { + BigQueryOptions.Builder bigqueryBuilder = BigQueryOptions.newBuilder(); + bigqueryBuilder.setCredentials(getCredentialsFromSecretsManager()); + return bigqueryBuilder.build().getService(); + } + + static ResourceManager getResourceManagerClient() + throws IOException + { + ResourceManagerOptions.Builder resourceManagerBuilder = ResourceManagerOptions.newBuilder(); + resourceManagerBuilder.setCredentials(getCredentialsFromSecretsManager()); + return resourceManagerBuilder.build().getService(); + } + + static String getEnvBigQueryCredsSmId() + { + return getEnvVar(BigQueryConstants.ENV_BIG_QUERY_CREDS_SM_ID); + } + + static String getEnvVar(String envVar) + { + String var = System.getenv(envVar); + if (var == null || var.length() == 0) { + throw new IllegalArgumentException("Lambda Environment Variable " + envVar + " has not been populated! "); + } + return var; + } + + /** + * Gets the project name that exists within Google Cloud Platform that contains the datasets that we wish to query. + * The Lambda environment variables are first inspected and if it does not exist, then we take it from the catalog + * name in the request. + * + * @param catalogNameFromRequest The Catalog Name from the request that is passed in from the Athena Connector framework. + * @return The project name. + */ + static String getProjectName(String catalogNameFromRequest) + { + if (System.getenv(BigQueryConstants.GCP_PROJECT_ID) != null) { + return System.getenv(BigQueryConstants.GCP_PROJECT_ID); + } + return catalogNameFromRequest; + } + + /** + * Gets the project name that exists within Google Cloud Platform that contains the datasets that we wish to query. + * The Lambda environment variables are first inspected and if it does not exist, then we take it from the catalog + * name in the request. + * + * @param request The {@link MetadataRequest} from the request that is passed in from the Athena Connector framework. + * @return The project name. + */ + static String getProjectName(MetadataRequest request) + { + return getProjectName(request.getCatalogName()); + } + + /** + * BigQuery is case sensitive for its Project and Dataset Names. This function will return the first + * case insensitive match. + * + * @param projectName The dataset name we want to look up. The project name must be case correct. + * @return A case correct dataset name. + */ + static String fixCaseForDatasetName(String projectName, String datasetName, BigQuery bigQuery) + { + Page response = bigQuery.listDatasets(projectName); + for (Dataset dataset : response.iterateAll()) { + if (dataset.getDatasetId().getDataset().equalsIgnoreCase(datasetName)) { + return dataset.getDatasetId().getDataset(); + } + } + + throw new IllegalArgumentException("Google Dataset with name " + datasetName + + " could not be found in Project " + projectName + " in GCP. "); + } + + static String fixCaseForTableName(String projectName, String datasetName, String tableName, BigQuery bigQuery) + { + Page
response = bigQuery.listTables(DatasetId.of(projectName, datasetName)); + for (Table table : response.iterateAll()) { + if (table.getTableId().getTable().equalsIgnoreCase(tableName)) { + return table.getTableId().getTable(); + } + } + throw new IllegalArgumentException("Google Table with name " + datasetName + + " could not be found in Project " + projectName + " in GCP. "); + } + + static Object getObjectFromFieldValue(String fieldName, FieldValue fieldValue, ArrowType arrowType) + { + if (fieldValue == null || fieldValue.isNull() || fieldValue.getValue().equals("null")) { + return null; + } + switch (Types.getMinorTypeForArrowType(arrowType)) { + case TIMESTAMPMILLI: + //getTimestampValue() returns a long in microseconds. Return it in Milliseconds which is how its stored. + return fieldValue.getTimestampValue() / 1000; + case SMALLINT: + case TINYINT: + case INT: + case BIGINT: + return fieldValue.getLongValue(); + case DECIMAL: + return fieldValue.getNumericValue(); + case BIT: + return fieldValue.getBooleanValue(); + case FLOAT4: + case FLOAT8: + return fieldValue.getDoubleValue(); + case VARCHAR: + return fieldValue.getStringValue(); + //TODO: Support complex types. + default: + throw new IllegalArgumentException("Unknown type has been encountered: Field Name: " + fieldName + + " Field Type: " + arrowType.toString() + " MinorType: " + Types.getMinorTypeForArrowType(arrowType)); + } + } + + static ArrowType translateToArrowType(LegacySQLTypeName type) + { + switch (type.getStandardType()) { + case BOOL: + return new ArrowType.Bool(); + /** A 64-bit signed integer value. */ + case INT64: + return new ArrowType.Int(64, true); + /** A 64-bit IEEE binary floating-point value. */ + case FLOAT64: + return new ArrowType.FloatingPoint(FloatingPointPrecision.DOUBLE); + /** A decimal value with 38 digits of precision and 9 digits of scale. */ + case NUMERIC: + return new ArrowType.Decimal(38, 9); + /** Variable-length character (Unicode) data. */ + case STRING: + return new ArrowType.Utf8(); + /** Variable-length binary data. */ + case BYTES: + return new ArrowType.Binary(); + /** Container of ordered fields each with a type (required) and field name (optional). */ + case STRUCT: + return new ArrowType.Struct(); + /** Ordered list of zero or more elements of any non-array type. */ + case ARRAY: + return new ArrowType.List(); + /** + * Represents an absolute point in time, with microsecond precision. Values range between the + * years 1 and 9999, inclusive. + */ + case TIMESTAMP: + return new ArrowType.Timestamp(TimeUnit.MILLISECOND, null); + /** Represents a logical calendar date. Values range between the years 1 and 9999, inclusive. */ + case DATE: + return new ArrowType.Date(DateUnit.DAY); + /** Represents a time, independent of a specific date, to microsecond precision. */ + case TIME: + return new ArrowType.Time(TimeUnit.MILLISECOND, 32); + /** Represents a year, month, day, hour, minute, second, and subsecond (microsecond precision). */ + case DATETIME: + return new ArrowType.Date(DateUnit.MILLISECOND); + /** Represents a set of geographic points, represented as a Well Known Text (WKT) string. */ + case GEOGRAPHY: + return new ArrowType.Utf8(); + } + throw new IllegalArgumentException("Unable to map Google Type of StandardType: " + type.getStandardType().toString() + + " NonStandardType: " + type.name()); + } +} diff --git a/athena-bigquery/src/test/java/com/amazonaws/athena/connectors/bigquery/BigQueryMetadataHandlerTest.java b/athena-bigquery/src/test/java/com/amazonaws/athena/connectors/bigquery/BigQueryMetadataHandlerTest.java new file mode 100644 index 0000000000..64311daec8 --- /dev/null +++ b/athena-bigquery/src/test/java/com/amazonaws/athena/connectors/bigquery/BigQueryMetadataHandlerTest.java @@ -0,0 +1,184 @@ +/*- + * #%L + * athena-bigquery + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +package com.amazonaws.athena.connectors.bigquery; + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesResponse; +import com.google.cloud.bigquery.BigQuery; +import com.google.cloud.bigquery.Dataset; +import com.google.cloud.bigquery.DatasetId; +import com.google.cloud.bigquery.Schema; +import com.google.cloud.bigquery.StandardTableDefinition; +import com.google.cloud.bigquery.Table; +import com.google.cloud.bigquery.TableId; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.Collections; +import java.util.HashMap; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class BigQueryMetadataHandlerTest +{ + private static final String QUERY_ID = "queryId"; + private static final String CATALOG = "catalog"; + private static final TableName TABLE_NAME = new TableName("dataset1", "table1"); + + @Mock + BigQuery bigQuery; + + private BigQueryMetadataHandler bigQueryMetadataHandler; + + private BlockAllocator blockAllocator; + + @Before + public void setUp() + { + MockitoAnnotations.initMocks(this); + bigQueryMetadataHandler = new BigQueryMetadataHandler(bigQuery); + blockAllocator = new BlockAllocatorImpl(); + } + + @After + public void tearDown() + { + blockAllocator.close(); + } + + @Test + public void testDoListSchemaNames() + { + final int numDatasets = 5; + BigQueryPage datasetPage = + new BigQueryPage<>(BigQueryTestUtils.getDatasetList(BigQueryTestUtils.PROJECT_1_NAME, numDatasets)); + when(bigQuery.listDatasets(any(String.class))).thenReturn(datasetPage); + + //This will test case insenstivity + ListSchemasRequest request = new ListSchemasRequest(BigQueryTestUtils.FEDERATED_IDENTITY, + QUERY_ID, BigQueryTestUtils.PROJECT_1_NAME.toLowerCase()); + ListSchemasResponse schemaNames = bigQueryMetadataHandler.doListSchemaNames(blockAllocator, request); + + assertNotNull(schemaNames); + assertEquals("Schema count does not match!", numDatasets, schemaNames.getSchemas().size()); + } + + @Test + public void testDoListTables() + { + //Build mocks for Datasets + final int numDatasets = 5; + BigQueryPage datasetPage = + new BigQueryPage<>(BigQueryTestUtils.getDatasetList(BigQueryTestUtils.PROJECT_1_NAME, numDatasets)); + when(bigQuery.listDatasets(any(String.class))).thenReturn(datasetPage); + + //Get the first dataset name. + String datasetName = datasetPage.iterateAll().iterator().next().getDatasetId().getDataset(); + + final int numTables = 5; + BigQueryPage
tablesPage = + new BigQueryPage<>(BigQueryTestUtils.getTableList(BigQueryTestUtils.PROJECT_1_NAME, + datasetName, numTables)); + + when(bigQuery.listTables(any(DatasetId.class))).thenReturn(tablesPage); + + //This will test case insenstivity + ListTablesRequest request = new ListTablesRequest(BigQueryTestUtils.FEDERATED_IDENTITY, + QUERY_ID, BigQueryTestUtils.PROJECT_1_NAME.toLowerCase(), + datasetName); + ListTablesResponse tableNames = bigQueryMetadataHandler.doListTables(blockAllocator, request); + + assertNotNull(tableNames); + assertEquals("Schema count does not match!", numTables, tableNames.getTables().size()); + } + + @Test + public void testDoGetTable() + { + //Build mocks for Datasets + final int numDatasets = 5; + BigQueryPage datasetPage = + new BigQueryPage<>(BigQueryTestUtils.getDatasetList(BigQueryTestUtils.PROJECT_1_NAME, numDatasets)); + when(bigQuery.listDatasets(any(String.class))).thenReturn(datasetPage); + + //Get the first dataset name. + String datasetName = datasetPage.iterateAll().iterator().next().getDatasetId().getDataset(); + + //Build mocks for Tables + final int numTables = 5; + BigQueryPage
tablesPage = + new BigQueryPage<>(BigQueryTestUtils.getTableList(BigQueryTestUtils.PROJECT_1_NAME, + datasetName, numTables)); + + String tableName = tablesPage.iterateAll().iterator().next().getTableId().getTable(); + + when(bigQuery.listTables(any(DatasetId.class))).thenReturn(tablesPage); + + Schema tableSchema = BigQueryTestUtils.getTestSchema(); + StandardTableDefinition tableDefinition = StandardTableDefinition.newBuilder() + .setSchema(tableSchema).build(); + + Table table = mock(Table.class); + when(table.getTableId()).thenReturn(TableId.of(BigQueryTestUtils.PROJECT_1_NAME, datasetName, tableName)); + when(table.getDefinition()).thenReturn(tableDefinition); + when(bigQuery.getTable(any(TableId.class))).thenReturn(table); + + //Make the call + GetTableRequest getTableRequest = new GetTableRequest(BigQueryTestUtils.FEDERATED_IDENTITY, + QUERY_ID, BigQueryTestUtils.PROJECT_1_NAME, + new TableName(datasetName, tableName)); + GetTableResponse response = bigQueryMetadataHandler.doGetTable(blockAllocator, getTableRequest); + + assertNotNull(response); + + //Number of Fields + assertEquals(tableSchema.getFields().size(), response.getSchema().getFields().size()); + } + + @Test + public void testDoGetSplits() + { + GetSplitsRequest request = new GetSplitsRequest(BigQueryTestUtils.FEDERATED_IDENTITY, + QUERY_ID, CATALOG, TABLE_NAME, + mock(Block.class), Collections.emptyList(), new Constraints(new HashMap<>()), null); + GetSplitsResponse response = bigQueryMetadataHandler.doGetSplits(blockAllocator, request); + + assertNotNull(response); + } +} diff --git a/athena-bigquery/src/test/java/com/amazonaws/athena/connectors/bigquery/BigQueryPage.java b/athena-bigquery/src/test/java/com/amazonaws/athena/connectors/bigquery/BigQueryPage.java new file mode 100644 index 0000000000..64fdad325d --- /dev/null +++ b/athena-bigquery/src/test/java/com/amazonaws/athena/connectors/bigquery/BigQueryPage.java @@ -0,0 +1,86 @@ +/*- + * #%L + * athena-bigquery + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +package com.amazonaws.athena.connectors.bigquery; + +import com.google.api.gax.paging.Page; + +import java.util.Collection; +import java.util.Iterator; + +/** + * This class is a wrapper around the {@link Page} class as a convient way to create Pages in unit tests. + * + * @param The type of object that is being returned from a Google BigQuery API call. For example, getDatasets(). + */ +class BigQueryPage + implements Page +{ + final Collection collection; + + BigQueryPage(Collection collection) + { + this.collection = collection; + } + + @Override + public boolean hasNextPage() + { + return false; + } + + @Override + public String getNextPageToken() + { + return null; + } + + @Override + public Page getNextPage() + { + return null; + } + + @Override + public Iterable iterateAll() + { + return new Iterable() + { + @Override + public Iterator iterator() + { + return collection.iterator(); + } + }; + } + + @Override + public Iterable getValues() + { + return new Iterable() + { + @Override + public Iterator iterator() + { + return collection.iterator(); + } + }; + } +} diff --git a/athena-bigquery/src/test/java/com/amazonaws/athena/connectors/bigquery/BigQueryRecordHandlerTest.java b/athena-bigquery/src/test/java/com/amazonaws/athena/connectors/bigquery/BigQueryRecordHandlerTest.java new file mode 100644 index 0000000000..f5555e5fa1 --- /dev/null +++ b/athena-bigquery/src/test/java/com/amazonaws/athena/connectors/bigquery/BigQueryRecordHandlerTest.java @@ -0,0 +1,314 @@ +/*- + * #%L + * athena-bigquery + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +package com.amazonaws.athena.connectors.bigquery; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.amazonaws.athena.connector.lambda.data.S3BlockSpillReader; +import com.amazonaws.athena.connector.lambda.data.S3BlockSpiller; +import com.amazonaws.athena.connector.lambda.data.SpillConfig; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.ConstraintEvaluator; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.domain.spill.S3SpillLocation; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.athena.connector.lambda.security.EncryptionKey; +import com.amazonaws.athena.connector.lambda.security.EncryptionKeyFactory; +import com.amazonaws.athena.connector.lambda.security.LocalKeyFactory; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.PutObjectResult; +import com.amazonaws.services.s3.model.S3Object; +import com.amazonaws.services.s3.model.S3ObjectInputStream; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.google.api.gax.paging.Page; +import com.google.cloud.bigquery.BigQuery; +import com.google.cloud.bigquery.Dataset; +import com.google.cloud.bigquery.DatasetId; +import com.google.cloud.bigquery.FieldValue; +import com.google.cloud.bigquery.FieldValueList; +import com.google.cloud.bigquery.Job; +import com.google.cloud.bigquery.JobInfo; +import com.google.cloud.bigquery.Table; +import com.google.cloud.bigquery.TableResult; +import com.google.common.io.ByteStreams; +import org.apache.arrow.vector.complex.reader.FieldReader; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.arrow.vector.types.pojo.Schema; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.invocation.InvocationOnMock; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class BigQueryRecordHandlerTest +{ + private static final Logger logger = LoggerFactory.getLogger(BigQueryRecordHandlerTest.class); + + private String bucket = "bucket"; + private String prefix = "prefix"; + + @Mock + BigQuery bigQuery; + + @Mock + AWSSecretsManager awsSecretsManager; + + @Mock + private AmazonAthena athena; + + private BigQueryRecordHandler bigQueryRecordHandler; + + private BlockAllocator allocator; + private List mockS3Storage = new ArrayList<>(); + private AmazonS3 amazonS3; + private S3BlockSpiller spillWriter; + private S3BlockSpillReader spillReader; + private Schema schemaForRead; + private EncryptionKeyFactory keyFactory = new LocalKeyFactory(); + private EncryptionKey encryptionKey = keyFactory.create(); + private SpillConfig spillConfig; + private String queryId = UUID.randomUUID().toString(); + private S3SpillLocation s3SpillLocation = S3SpillLocation.newBuilder() + .withBucket(UUID.randomUUID().toString()) + .withSplitId(UUID.randomUUID().toString()) + .withQueryId(queryId) + .withIsDirectory(true) + .build(); + + @Before + public void init() + { + logger.info("Starting init."); + MockitoAnnotations.initMocks(this); + + allocator = new BlockAllocatorImpl(); + amazonS3 = mock(AmazonS3.class); + + mockS3Client(); + + //Create Spill config + spillConfig = SpillConfig.newBuilder() + .withEncryptionKey(encryptionKey) + //This will be enough for a single block + .withMaxBlockBytes(100000) + //This will force the writer to spill. + .withMaxInlineBlockBytes(100) + //Async Writing. + .withNumSpillThreads(0) + .withRequestId(UUID.randomUUID().toString()) + .withSpillLocation(s3SpillLocation) + .build(); + + schemaForRead = new Schema(BigQueryTestUtils.getTestSchemaFieldsArrow()); + spillWriter = new S3BlockSpiller(amazonS3, spillConfig, allocator, schemaForRead, ConstraintEvaluator.emptyEvaluator()); + spillReader = new S3BlockSpillReader(amazonS3, allocator); + + //Mock the BigQuery Client to return Datasets, and Table Schema information. + BigQueryPage datasets = new BigQueryPage(BigQueryTestUtils.getDatasetList(BigQueryTestUtils.PROJECT_1_NAME, 2)); + when(bigQuery.listDatasets(any(String.class))).thenReturn(datasets); + BigQueryPage
tables = new BigQueryPage
(BigQueryTestUtils.getTableList(BigQueryTestUtils.PROJECT_1_NAME, "dataset1", 2)); + when(bigQuery.listTables(any(DatasetId.class))).thenReturn(tables); + + //The class we want to test. + bigQueryRecordHandler = new BigQueryRecordHandler(amazonS3, awsSecretsManager, athena, bigQuery); + + logger.info("Completed init."); + } + + @Test + public void testReadWithConstraint() + throws Exception + { + try (ReadRecordsRequest request = new ReadRecordsRequest( + BigQueryTestUtils.FEDERATED_IDENTITY, + BigQueryTestUtils.PROJECT_1_NAME, + "queryId", + new TableName("dataset1", "table1"), + BigQueryTestUtils.getBlockTestSchema(), + Split.newBuilder(S3SpillLocation.newBuilder() + .withBucket(bucket) + .withPrefix(prefix) + .withSplitId(UUID.randomUUID().toString()) + .withQueryId(UUID.randomUUID().toString()) + .withIsDirectory(true) + .build(), + keyFactory.create()).build(), + new Constraints(Collections.EMPTY_MAP), + 0, //This is ignored when directly calling readWithConstraints. + 0)) { //This is ignored when directly calling readWithConstraints. + //Always return try for the evaluator to keep all rows. + ConstraintEvaluator evaluator = mock(ConstraintEvaluator.class); + when(evaluator.apply(any(String.class), any(Object.class))).thenAnswer( + (InvocationOnMock invocationOnMock) -> { + return true; + } + ); + + //Populate the schema and data that the mocked Google BigQuery client will return. + com.google.cloud.bigquery.Schema tableSchema = BigQueryTestUtils.getTestSchema(); + List tableRows = Arrays.asList( + BigQueryTestUtils.getBigQueryFieldValueList(false, 1000, "test1", 123123.12312), + BigQueryTestUtils.getBigQueryFieldValueList(true, 500, "test2", 5345234.22111), + BigQueryTestUtils.getBigQueryFieldValueList(false, 700, "test3", 324324.23423), + BigQueryTestUtils.getBigQueryFieldValueList(true, 900, null, null), + BigQueryTestUtils.getBigQueryFieldValueList(null, null, "test5", 2342.234234), + BigQueryTestUtils.getBigQueryFieldValueList(true, 1200, "test6", 1123.12312), + BigQueryTestUtils.getBigQueryFieldValueList(false, 100, "test7", 1313.12312), + BigQueryTestUtils.getBigQueryFieldValueList(true, 120, "test8", 12313.1312), + BigQueryTestUtils.getBigQueryFieldValueList(false, 300, "test9", 12323.1312) + ); + Page fieldValueList = new BigQueryPage<>(tableRows); + TableResult result = new TableResult(tableSchema, tableRows.size(), fieldValueList); + + //Mock out the Google BigQuery Job. + Job mockBigQueryJob = mock(Job.class); + when(mockBigQueryJob.isDone()).thenReturn(false).thenReturn(true); + when(mockBigQueryJob.getQueryResults()).thenReturn(result); + when(bigQuery.create(any(JobInfo.class))).thenReturn(mockBigQueryJob); + + QueryStatusChecker queryStatusChecker = mock(QueryStatusChecker.class); + when(queryStatusChecker.isQueryRunning()).thenReturn(true); + + //Execute the test + bigQueryRecordHandler.readWithConstraint(spillWriter, request, queryStatusChecker); + + //Ensure that there was a spill so that we can read the spilled block. + assertTrue(spillWriter.spilled()); + //Calling getSpillLocations() forces a flush. + assertEquals(1, spillWriter.getSpillLocations().size()); + + //Read the spilled block + Block block = spillReader.read(s3SpillLocation, encryptionKey, schemaForRead); + + assertEquals("The number of rows expected do not match!", tableRows.size(), block.getRowCount()); + validateBlock(block, tableRows); + } + } + + private void validateBlock(Block block, List tableRows) + { + //Iterator through the fields + for (Field field : block.getFields()) { + FieldReader fieldReader = block.getFieldReader(field.getName()); + int currentCount = 0; + //Iterator through the rows and match up with the block + for (FieldValueList tableRow : tableRows) { + FieldValue orgValue = tableRow.get(field.getName()); + fieldReader.setPosition(currentCount); + currentCount++; + + logger.debug("comparing: {} with {}", orgValue.getValue(), fieldReader.readObject()); + + //Check for null values. + if ((orgValue.getValue() == null || fieldReader.readObject() == null)) { + assertTrue(orgValue.isNull()); + assertFalse(fieldReader.isSet()); + continue; + } + + //Check regular values. + Types.MinorType type = Types.getMinorTypeForArrowType(field.getType()); + switch (type) { + case INT: + assertEquals(orgValue.getLongValue(), (long) fieldReader.readInteger()); + break; + case BIT: + assertEquals(orgValue.getBooleanValue(), fieldReader.readBoolean()); + break; + case FLOAT4: + assertEquals(orgValue.getDoubleValue(), fieldReader.readFloat(), 0.001); + break; + case FLOAT8: + assertEquals(orgValue.getDoubleValue(), fieldReader.readDouble(), 0.001); + break; + case VARCHAR: + assertEquals(orgValue.getStringValue(), fieldReader.readText().toString()); + break; + default: + throw new RuntimeException("No validation configured for field " + field.getName() + ":" + type + " " + field.getChildren()); + } + } + } + } + + //Mocks the S3 client by storing any putObjects() and returning the object when getObject() is called. + private void mockS3Client() + { + when(amazonS3.putObject(anyObject(), anyObject(), anyObject(), anyObject())) + .thenAnswer((InvocationOnMock invocationOnMock) -> { + InputStream inputStream = (InputStream) invocationOnMock.getArguments()[2]; + ByteHolder byteHolder = new ByteHolder(); + byteHolder.setBytes(ByteStreams.toByteArray(inputStream)); + mockS3Storage.add(byteHolder); + return mock(PutObjectResult.class); + }); + + when(amazonS3.getObject(anyString(), anyString())) + .thenAnswer((InvocationOnMock invocationOnMock) -> { + S3Object mockObject = mock(S3Object.class); + ByteHolder byteHolder = mockS3Storage.get(0); + mockS3Storage.remove(0); + when(mockObject.getObjectContent()).thenReturn( + new S3ObjectInputStream( + new ByteArrayInputStream(byteHolder.getBytes()), null)); + return mockObject; + }); + } + + private class ByteHolder + { + private byte[] bytes; + + void setBytes(byte[] bytes) + { + this.bytes = bytes; + } + + byte[] getBytes() + { + return bytes; + } + } +} diff --git a/athena-bigquery/src/test/java/com/amazonaws/athena/connectors/bigquery/BigQuerySqlUtilsTest.java b/athena-bigquery/src/test/java/com/amazonaws/athena/connectors/bigquery/BigQuerySqlUtilsTest.java new file mode 100644 index 0000000000..ee0564cb80 --- /dev/null +++ b/athena-bigquery/src/test/java/com/amazonaws/athena/connectors/bigquery/BigQuerySqlUtilsTest.java @@ -0,0 +1,115 @@ +/*- + * #%L + * athena-bigquery + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +package com.amazonaws.athena.connectors.bigquery; + +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.AllOrNoneValueSet; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.domain.predicate.EquatableValueSet; +import com.amazonaws.athena.connector.lambda.domain.predicate.Marker; +import com.amazonaws.athena.connector.lambda.domain.predicate.Range; +import com.amazonaws.athena.connector.lambda.domain.predicate.SortedRangeSet; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.arrow.vector.types.pojo.Schema; +import org.junit.Test; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +import static org.junit.Assert.assertEquals; + +public class BigQuerySqlUtilsTest +{ + static final TableName tableName = new TableName("schema", "table"); + static final Split split = null; + + static final ArrowType BOOLEAN_TYPE = ArrowType.Bool.INSTANCE; + static final ArrowType INT_TYPE = new ArrowType.Int(32, true); + + @Test + public void testSqlWithConstraintsEquality() + throws Exception + { + Map constraintMap = new LinkedHashMap<>(); + constraintMap.put("bool1", EquatableValueSet.newBuilder(new BlockAllocatorImpl(), BOOLEAN_TYPE, + true, false).add(false).build()); + constraintMap.put("int1", EquatableValueSet.newBuilder(new BlockAllocatorImpl(), INT_TYPE, + true, false).add(14).build()); + constraintMap.put("nullableField", EquatableValueSet.newBuilder(new BlockAllocatorImpl(), INT_TYPE, + true, true).build()); + + try (Constraints constraints = new Constraints(constraintMap)) { + String sql = BigQuerySqlUtils.buildSqlFromSplit(tableName, makeSchema(constraintMap), constraints, split); + assertEquals("SELECT bool1,int1,nullableField from schema.table WHERE (bool1 = false) AND (int1 = 14) AND (nullableField is null)", sql); + } + } + + @Test + public void testSqlWithConstraintsRanges() + throws Exception + { + Map constraintMap = new LinkedHashMap<>(); + ValueSet rangeSet = SortedRangeSet.newBuilder(INT_TYPE, true).add(new Range(Marker.above(new BlockAllocatorImpl(), INT_TYPE, 10), + Marker.exactly(new BlockAllocatorImpl(), INT_TYPE, 20))).build(); + + ValueSet isNullRangeSet = SortedRangeSet.newBuilder(INT_TYPE, true).build(); + + ValueSet isNonNullRangeSet = SortedRangeSet.newBuilder(INT_TYPE, false).add( + new Range(Marker.lowerUnbounded(new BlockAllocatorImpl(), INT_TYPE), + Marker.upperUnbounded(new BlockAllocatorImpl(), INT_TYPE))) + .build(); + + constraintMap.put("integerRange", rangeSet); + constraintMap.put("isNullRange", isNullRangeSet); + constraintMap.put("isNotNullRange", isNonNullRangeSet); + + try (Constraints constraints = new Constraints(constraintMap)) { + String sql = BigQuerySqlUtils.buildSqlFromSplit(tableName, makeSchema(constraintMap), constraints, split); + assertEquals("SELECT integerRange,isNullRange,isNotNullRange from schema.table WHERE (integerRange > 10) AND (integerRange <= 20) AND (isNullRange is null) AND (isNotNullRange is not null)", sql); + } + } + + private Schema makeSchema(Map constraintMap) + { + SchemaBuilder builder = new SchemaBuilder(); + for (Map.Entry field : constraintMap.entrySet()) { + ArrowType.ArrowTypeID typeId = field.getValue().getType().getTypeID(); + switch (typeId) { + case Int: + builder.addIntField(field.getKey()); + break; + case Bool: + builder.addBitField(field.getKey()); + break; + case Utf8: + builder.addStringField(field.getKey()); + default: + throw new UnsupportedOperationException("Type Not Implemented: " + typeId.name()); + } + } + return builder.build(); + } +} diff --git a/athena-bigquery/src/test/java/com/amazonaws/athena/connectors/bigquery/BigQueryTestUtils.java b/athena-bigquery/src/test/java/com/amazonaws/athena/connectors/bigquery/BigQueryTestUtils.java new file mode 100644 index 0000000000..168b86ac20 --- /dev/null +++ b/athena-bigquery/src/test/java/com/amazonaws/athena/connectors/bigquery/BigQueryTestUtils.java @@ -0,0 +1,143 @@ +/*- + * #%L + * athena-bigquery + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +package com.amazonaws.athena.connectors.bigquery; + +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.security.FederatedIdentity; +import com.google.cloud.bigquery.Dataset; +import com.google.cloud.bigquery.DatasetId; +import com.google.cloud.bigquery.Field; +import com.google.cloud.bigquery.FieldList; +import com.google.cloud.bigquery.FieldValue; +import com.google.cloud.bigquery.FieldValueList; +import com.google.cloud.bigquery.LegacySQLTypeName; +import com.google.cloud.bigquery.Schema; +import com.google.cloud.bigquery.Table; +import com.google.cloud.bigquery.TableId; +import org.apache.arrow.vector.types.FloatingPointPrecision; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.arrow.vector.types.pojo.FieldType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class BigQueryTestUtils +{ + public static final FederatedIdentity FEDERATED_IDENTITY = new FederatedIdentity("id", "principal", "account"); + public static final String BOOL_FIELD_NAME_1 = "bool1"; + public static final String INTEGER_FIELD_NAME_1 = "int1"; + public static final String STRING_FIELD_NAME_1 = "string1"; + public static final String FLOAT_FIELD_NAME_1 = "float1"; + + private BigQueryTestUtils() { + } + + public static final String PROJECT_1_NAME = "testProject"; + + //Returns a list of mocked Datasets. + static List getDatasetList(String projectName, int numDatasets) + { + List datasetList = new ArrayList<>(); + for (int i = 0; i < numDatasets; i++) { + Dataset dataset1 = mock(Dataset.class); + when(dataset1.getDatasetId()).thenReturn(DatasetId.of(projectName, "dataset" + i)); + when(dataset1.getFriendlyName()).thenReturn("dataset" + i); + datasetList.add(dataset1); + } + return datasetList; + } + + //Returns a list of mocked Tables + static List
getTableList(String projectName, String dataset, int numTables) + { + List
tableList = new ArrayList<>(); + for (int i = 0; i < numTables; i++) { + Table table = mock(Table.class); + when(table.getTableId()).thenReturn(TableId.of(projectName, dataset, "table" + i)); + tableList.add(table); + } + return tableList; + } + + //Returns the schema by returning a list of fields in Google BigQuery Format. + static List getTestSchemaFields() + { + return Arrays.asList(Field.of(BOOL_FIELD_NAME_1, LegacySQLTypeName.BOOLEAN), + Field.of(INTEGER_FIELD_NAME_1, LegacySQLTypeName.INTEGER), + Field.of(STRING_FIELD_NAME_1, LegacySQLTypeName.STRING), + Field.of(FLOAT_FIELD_NAME_1, LegacySQLTypeName.FLOAT) + ); + } + + static Schema getTestSchema() + { + return Schema.of(getTestSchemaFields()); + } + + //Gets the schema in Arrow Format. + static org.apache.arrow.vector.types.pojo.Schema getBlockTestSchema() + { + return SchemaBuilder.newBuilder() + .addBitField(BOOL_FIELD_NAME_1) + .addIntField(INTEGER_FIELD_NAME_1) + .addStringField(STRING_FIELD_NAME_1) + .addFloat8Field(FLOAT_FIELD_NAME_1) + .build(); + } + + static Collection getTestSchemaFieldsArrow() + { + return Arrays.asList( + new org.apache.arrow.vector.types.pojo.Field(BOOL_FIELD_NAME_1, + FieldType.nullable(ArrowType.Bool.INSTANCE), null), + new org.apache.arrow.vector.types.pojo.Field(INTEGER_FIELD_NAME_1, + FieldType.nullable(new ArrowType.Int(32, true)), null), + new org.apache.arrow.vector.types.pojo.Field(STRING_FIELD_NAME_1, + FieldType.nullable(new ArrowType.Utf8()), null), + new org.apache.arrow.vector.types.pojo.Field(FLOAT_FIELD_NAME_1, + FieldType.nullable(new ArrowType.FloatingPoint(FloatingPointPrecision.DOUBLE)), null) + ); + } + + static List generateBigQueryRowValue(Boolean bool, Integer integer, String string, Double floatVal) + { + return Arrays.asList( + //Primitives are stored as Strings. + FieldValue.of(FieldValue.Attribute.PRIMITIVE, bool == null ? null : String.valueOf(bool)), + FieldValue.of(FieldValue.Attribute.PRIMITIVE, integer == null ? null : String.valueOf(integer)), + //Timestamps are stored as a number, where the integer component of the number is seconds since epoch + //and the microsecond part is the decimal part. + FieldValue.of(FieldValue.Attribute.PRIMITIVE, string), + FieldValue.of(FieldValue.Attribute.PRIMITIVE, floatVal == null ? null : String.valueOf(floatVal)) + ); + } + + static FieldValueList getBigQueryFieldValueList(Boolean bool, Integer integer, String string, Double floatVal) + { + return FieldValueList.of(generateBigQueryRowValue(bool, integer, string, floatVal), + FieldList.of(getTestSchemaFields())); + } +} diff --git a/athena-cloudwatch-metrics/LICENSE.txt b/athena-cloudwatch-metrics/LICENSE.txt new file mode 100644 index 0000000000..418de4c108 --- /dev/null +++ b/athena-cloudwatch-metrics/LICENSE.txt @@ -0,0 +1,174 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. \ No newline at end of file diff --git a/athena-cloudwatch-metrics/README.md b/athena-cloudwatch-metrics/README.md new file mode 100644 index 0000000000..caed435c27 --- /dev/null +++ b/athena-cloudwatch-metrics/README.md @@ -0,0 +1,85 @@ +# Amazon Athena Cloudwatch Metrics Connector + +This connector enables Amazon Athena to communicate with Cloudwatch Metrics, making your metrics data accessible via SQL. + +## Usage + +### Parameters + +The Athena Cloudwatch Metrics Connector exposes several configuration options via Lambda environment variables. More detail on the available parameters can be found below. + +1. **spill_bucket** - When the data returned by your Lambda function exceeds Lambda’s limits, this is the bucket that the data will be written to for Athena to read the excess from. (e.g. my_bucket) +2. **spill_prefix** - (Optional) Defaults to sub-folder in your bucket called 'athena-federation-spill'. Used in conjunction with spill_bucket, this is the path within the above bucket that large responses are spilled to. You should configure an S3 lifecycle on this location to delete old spills after X days/Hours. +3. **kms_key_id** - (Optional) By default any data that is spilled to S3 is encrypted using AES-GCM and a randomly generated key. Setting a KMS Key ID allows your Lambda function to use KMS for key generation for a stronger source of encryption keys. (e.g. a7e63k4b-8loc-40db-a2a1-4d0en2cd8331) +4. **disable_spill_encryption** - (Optional) Defaults to False so that any data that is spilled to S3 is encrypted using AES-GMC either with a randomly generated key or using KMS to generate keys. Setting this to false will disable spill encryption. You may wish to disable this for improved performance, especially if your spill location in S3 uses S3 Server Side Encryption. (e.g. True or False) + +The connector also supports AIMD Congestion Control for handling throttling events from Cloudwatch via the Athena Query Federation SDK's ThrottlingInvoker construct. You can tweak the default throttling behavior by setting any of the below (optional) environment variables: + +1. **throttle_initial_delay_ms** - (Default: 10ms) This is the initial call delay applied after the first congestion event. +1. **throttle_max_delay_ms** - (Default: 1000ms) This is the max delay between calls. You can derive TPS by dividing it into 1000ms. +1. **throttle_decrease_factor** - (Default: 0.5) This is the factor by which we reduce our call rate. +1. **throttle_increase_ms** - (Default: 10ms) This is the rate at which we decrease the call delay. + + +### Databases & Tables + +The Athena Cloudwatch Metrics Connector maps your Namespaces, Dimensions, Metrics, and Metric Values into two tables in a single schema called "default". + +1. **metrics** - This table contains the available metrics as uniquely defined by a triple of namespace, set, name. More specifically, this table contains the following columns. + + * **namespace** - A VARCHAR containing the namespace. + * **metric_name** - A VARCHAR containing the metric name. + * **dimensions** - A LIST of STRUCTS comprised of dim_name (VARCHAR) and dim_value (VARCHAR). + * **statistic** - A List of VARCH statistics (e.g. p90, AVERAGE, etc..) avialable for the metric. + +1. **metric_samples** - This table contains the available metric samples for each metric named in the **metrics** table. More specifically, the table contains the following columns: + * **namespace** - A VARCHAR containing the namespace. + * **metric_name** - A VARCHAR containing the metric name. + * **dimensions** - A LIST of STRUCTS comprised of dim_name (VARCHAR) and dim_value (VARCHAR). + * **dim_name** - A VARCHAR convenience field used to easily filter on a single dimension name. + * **dim_value** - A VARCHAR convenience field used to easily filter on a single dimension value. + * **period** - An INT field representing the 'period' of the metric in seconds. (e.g. 60 second metric) + * **timestamp** - A BIGINT field representing the epoch time (in seconds) the metric sample is for. + * **value** - A FLOAT8 field containing the value of the sample. + * **statistic** - A VARCHAR containing the statistic type of the sample. (e.g. AVERAGE, p90, etc..) + +### Required Permissions + +Review the "Policies" section of the athena-cloudwatch-metrics.yaml file for full details on the IAM Policies required by this connector. A brief summary is below. + +1. S3 Write Access - In order to successfully handle large queries, the connector requires write access to a location in S3. +2. Cloudwatch Metrics ReadOnly - The connector uses this access to query your metrics data. +2. Cloudwatch Logs Write - The connector uses this access to write its own diagnostic logs. +1. Athena GetQueryExecution - The connector uses this access to fast-fail when the upstream Athena query has terminated. + +### Deploying The Connector + +To use this connector in your queries, navigate to AWS Serverless Application Repository and deploy a pre-built version of this connector. Alternatively, you can build and deploy this connector from source follow the below steps or use the more detailed tutorial in the athena-example module: + +1. From the athena-federation-sdk dir, run `mvn clean install` if you haven't already. +2. From the athena-cloudwatch-metrics dir, run `mvn clean install`. +3. From the athena-cloudwatch-metrics dir, run `../tools/publish.sh S3_BUCKET_NAME athena-cloudwatch-metrics` to publish the connector to your private AWS Serverless Application Repository. The S3_BUCKET in the command is where a copy of the connector's code will be stored for Serverless Application Repository to retrieve it. This will allow users with permission to do so, the ability to deploy instances of the connector via 1-Click form. Then navigate to [Serverless Application Repository](https://aws.amazon.com/serverless/serverlessrepo) +4. Try running a query like the one below in Athena: +```sql +-- Get the list of available metrics +select * from "lambda:"."default".metrics limit 100 + +-- Query the last 3 days of AWS/Lambda Invocations metrics +SELECT * +FROM "lambda:"."default".metric_samples +WHERE metric_name = 'Invocations' + AND namespace = 'AWS/Lambda' + AND statistic IN ( 'p90', 'Average' ) + AND period = 60 + AND timestamp BETWEEN To_unixtime(Now() - INTERVAL '3' day) AND + To_unixtime(Now()) +LIMIT 100; +``` + +## Performance + +The Athena Cloudwatch Metrics Connector will attempt to parallelize queries against Cloudwatch Metrics by parallelizing scans of the various metrics needed for your query. Predicate Pushdown is performed within the Lambda function and also within Cloudwatch Logs for certain time period , metric, namespace, and dimension filters. + +## License + +This project is licensed under the Apache-2.0 License. \ No newline at end of file diff --git a/athena-cloudwatch-metrics/athena-cloudwatch-metrics.yaml b/athena-cloudwatch-metrics/athena-cloudwatch-metrics.yaml new file mode 100644 index 0000000000..1d9d44e84f --- /dev/null +++ b/athena-cloudwatch-metrics/athena-cloudwatch-metrics.yaml @@ -0,0 +1,66 @@ +Transform: 'AWS::Serverless-2016-10-31' +Metadata: + 'AWS::ServerlessRepo::Application': + Name: AthenaCloudwatchMetricsConnector + Description: 'This connector enables Amazon Athena to communicate with Cloudwatch Metrics, making your metrics data accessible via SQL.' + Author: 'Amazon Athena' + SpdxLicenseId: Apache-2.0 + LicenseUrl: LICENSE.txt + ReadmeUrl: README.md + Labels: + - athena-federation + HomePageUrl: 'https://github.com/awslabs/aws-athena-query-federation' + SemanticVersion: 1.0.0 + SourceCodeUrl: 'https://github.com/awslabs/aws-athena-query-federation' +Parameters: + AthenaCatalogName: + Description: 'The name you will give to this catalog in Athena. It will also be used as the function name.' + Type: String + SpillBucket: + Description: 'The bucket where this function can spill data.' + Type: String + SpillPrefix: + Description: 'The bucket prefix where this function can spill large responses.' + Type: String + Default: athena-spill + LambdaTimeout: + Description: 'Maximum Lambda invocation runtime in seconds. (min 1 - 900 max)' + Default: 900 + Type: Number + LambdaMemory: + Description: 'Lambda memory in MB (min 128 - 3008 max).' + Default: 3008 + Type: Number + DisableSpillEncryption: + Description: "WARNING: If set to 'true' encryption for spilled data is disabled." + Default: 'false' + Type: String +Resources: + ConnectorConfig: + Type: 'AWS::Serverless::Function' + Properties: + Environment: + Variables: + disable_spill_encryption: !Ref DisableSpillEncryption + spill_bucket: !Ref SpillBucket + spill_prefix: !Ref SpillPrefix + FunctionName: !Ref AthenaCatalogName + Handler: "com.amazonaws.athena.connectors.cloudwatch.metrics.MetricsCompositeHandler" + CodeUri: "./target/athena-cloudwatch-metrics-1.0.jar" + Description: "Enables Amazon Athena to communicate with Cloudwatch Metrics, making your metrics data accessible via SQL" + Runtime: java8 + Timeout: !Ref LambdaTimeout + MemorySize: !Ref LambdaMemory + Policies: + - Statement: + - Action: + - cloudwatch:Describe* + - cloudwatch:Get* + - cloudwatch:List* + Effect: Allow + Resource: '*' + Version: '2012-10-17' + #S3CrudPolicy allows our connector to spill large responses to S3. You can optionally replace this pre-made policy + #with one that is more restrictive and can only 'put' but not read,delete, or overwrite files. + - S3CrudPolicy: + BucketName: !Ref SpillBucket \ No newline at end of file diff --git a/athena-cloudwatch-metrics/pom.xml b/athena-cloudwatch-metrics/pom.xml new file mode 100644 index 0000000000..b2bd8796f8 --- /dev/null +++ b/athena-cloudwatch-metrics/pom.xml @@ -0,0 +1,57 @@ + + + + aws-athena-query-federation + com.amazonaws + 1.0 + + 4.0.0 + + athena-cloudwatch-metrics + + + + com.amazonaws + aws-athena-federation-sdk + ${aws-athena-federation-sdk.version} + + + com.amazonaws + aws-java-sdk-cloudwatch + 1.11.490 + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.1 + + false + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + package + + shade + + + + + + + \ No newline at end of file diff --git a/athena-cloudwatch-metrics/src/main/java/com/amazonaws/athena/connectors/cloudwatch/metrics/DimensionSerDe.java b/athena-cloudwatch-metrics/src/main/java/com/amazonaws/athena/connectors/cloudwatch/metrics/DimensionSerDe.java new file mode 100644 index 0000000000..7752896ebe --- /dev/null +++ b/athena-cloudwatch-metrics/src/main/java/com/amazonaws/athena/connectors/cloudwatch/metrics/DimensionSerDe.java @@ -0,0 +1,93 @@ +/*- + * #%L + * athena-cloudwatch-metrics + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.cloudwatch.metrics; + +import com.amazonaws.services.cloudwatch.model.Dimension; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.IOException; +import java.util.List; + +/** + * Used to serialize and deserialize Cloudwatch Metrics Dimension objects. This is used + * when creating and processing Splits. + */ +public class DimensionSerDe +{ + protected static final String SERIALZIE_DIM_FIELD_NAME = "d"; + private static final ObjectMapper mapper = new ObjectMapper(); + + private DimensionSerDe() {} + + /** + * Serializes the provided List of Dimensions. + * + * @param dim The list of dimensions to serialize. + * @return A String containing the serialized list of Dimensions. + */ + public static String serialize(List dim) + { + try { + return mapper.writeValueAsString(new DimensionHolder(dim)); + } + catch (JsonProcessingException ex) { + throw new RuntimeException(ex); + } + } + + /** + * Deserializes the provided String into a List of Dimensions. + * + * @param serializeDim A serialized list of Dimensions. + * @return The List of Dimensions represented by the serialized string. + */ + public static List deserialize(String serializeDim) + { + try { + return mapper.readValue(serializeDim, DimensionHolder.class).getDimensions(); + } + catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + /** + * Helper which allows us to use Jackson's Object Mapper to serialize a List of Dimensions. + */ + private static class DimensionHolder + { + private final List dimensions; + + @JsonCreator + public DimensionHolder(@JsonProperty("dimensions") List dimensions) + { + this.dimensions = dimensions; + } + + @JsonProperty + public List getDimensions() + { + return dimensions; + } + } +} diff --git a/athena-cloudwatch-metrics/src/main/java/com/amazonaws/athena/connectors/cloudwatch/metrics/MetricUtils.java b/athena-cloudwatch-metrics/src/main/java/com/amazonaws/athena/connectors/cloudwatch/metrics/MetricUtils.java new file mode 100644 index 0000000000..862396bd58 --- /dev/null +++ b/athena-cloudwatch-metrics/src/main/java/com/amazonaws/athena/connectors/cloudwatch/metrics/MetricUtils.java @@ -0,0 +1,198 @@ +/*- + * #%L + * athena-cloudwatch-metrics + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.cloudwatch.metrics; + +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.predicate.ConstraintEvaluator; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.domain.predicate.Range; +import com.amazonaws.athena.connector.lambda.domain.predicate.SortedRangeSet; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.services.cloudwatch.model.Dimension; +import com.amazonaws.services.cloudwatch.model.DimensionFilter; +import com.amazonaws.services.cloudwatch.model.GetMetricDataRequest; +import com.amazonaws.services.cloudwatch.model.ListMetricsRequest; +import com.amazonaws.services.cloudwatch.model.Metric; +import com.amazonaws.services.cloudwatch.model.MetricDataQuery; +import com.amazonaws.services.cloudwatch.model.MetricStat; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Map; + +import static com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table.DIMENSION_NAME_FIELD; +import static com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table.DIMENSION_VALUE_FIELD; +import static com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table.METRIC_NAME_FIELD; +import static com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table.NAMESPACE_FIELD; +import static com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table.PERIOD_FIELD; +import static com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table.STATISTIC_FIELD; +import static com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table.TIMESTAMP_FIELD; + +/** + * Helper which prepares and filters Cloudwatch Metrics requests. + */ +public class MetricUtils +{ + private static final Logger logger = LoggerFactory.getLogger(MetricUtils.class); + + //this is a format required by Cloudwatch Metrics + private static final String METRIC_ID = "m1"; + + private MetricUtils() {} + + /** + * Filters metrics who have at least 1 metric dimension that matches DIMENSION_NAME_FIELD and DIMENSION_VALUE_FIELD filters. + * This is just an optimization and isn't fully correct. We depend on the calling engine to apply full constraints. Also + * filters metric name and namespace. + * + * @return True if the supplied metric contains at least 1 Dimension matching the evaluator. + */ + protected static boolean applyMetricConstraints(ConstraintEvaluator evaluator, Metric metric, String statistic) + { + if (!evaluator.apply(NAMESPACE_FIELD, metric.getNamespace())) { + return false; + } + + if (!evaluator.apply(METRIC_NAME_FIELD, metric.getMetricName())) { + return false; + } + + if (statistic != null && !evaluator.apply(STATISTIC_FIELD, statistic)) { + return false; + } + + for (Dimension next : metric.getDimensions()) { + if (evaluator.apply(DIMENSION_NAME_FIELD, next.getName()) && evaluator.apply(DIMENSION_VALUE_FIELD, next.getValue())) { + return true; + } + } + + if (metric.getDimensions().isEmpty() && + evaluator.apply(DIMENSION_NAME_FIELD, null) && + evaluator.apply(DIMENSION_VALUE_FIELD, null)) { + return true; + } + + return false; + } + + /** + * Attempts to push the supplied predicate constraints onto the Cloudwatch Metrics request. + */ + protected static void pushDownPredicate(Constraints constraints, ListMetricsRequest listMetricsRequest) + { + Map summary = constraints.getSummary(); + + ValueSet namespaceConstraint = summary.get(NAMESPACE_FIELD); + if (namespaceConstraint != null && namespaceConstraint.isSingleValue()) { + listMetricsRequest.setNamespace(namespaceConstraint.getSingleValue().toString()); + } + + ValueSet metricConstraint = summary.get(METRIC_NAME_FIELD); + if (metricConstraint != null && metricConstraint.isSingleValue()) { + listMetricsRequest.setMetricName(metricConstraint.getSingleValue().toString()); + } + + ValueSet dimensionNameConstraint = summary.get(DIMENSION_NAME_FIELD); + ValueSet dimensionValueConstraint = summary.get(DIMENSION_VALUE_FIELD); + if (dimensionNameConstraint != null && dimensionNameConstraint.isSingleValue() && + dimensionValueConstraint != null && dimensionValueConstraint.isSingleValue()) { + DimensionFilter filter = new DimensionFilter() + .withName(dimensionNameConstraint.getSingleValue().toString()) + .withValue(dimensionValueConstraint.getSingleValue().toString()); + listMetricsRequest.setDimensions(Collections.singletonList(filter)); + } + } + + /** + * Creates a Cloudwatch Metrics sample data request from the provided inputs + * + * @param readRecordsRequest The RecordReadRequest to make into a Cloudwatch Metrics Data request. + * @return The Cloudwatch Metrics Data request that matches the requested read operation. + */ + protected static GetMetricDataRequest makeGetMetricDataRequest(ReadRecordsRequest readRecordsRequest) + { + Split split = readRecordsRequest.getSplit(); + List dimensions = DimensionSerDe.deserialize(split.getProperty(DimensionSerDe.SERIALZIE_DIM_FIELD_NAME)); + GetMetricDataRequest dataRequest = new GetMetricDataRequest(); + com.amazonaws.services.cloudwatch.model.Metric metric = new com.amazonaws.services.cloudwatch.model.Metric(); + metric.setNamespace(split.getProperty(NAMESPACE_FIELD)); + metric.setMetricName(split.getProperty(METRIC_NAME_FIELD)); + + List dList = new ArrayList<>(); + for (Dimension nextDim : dimensions) { + dList.add(new Dimension().withName(nextDim.getName()).withValue(nextDim.getValue())); + } + metric.setDimensions(dList); + + MetricDataQuery mds = new MetricDataQuery() + .withMetricStat(new MetricStat() + .withMetric(metric) + .withPeriod(Integer.valueOf(split.getProperty(PERIOD_FIELD))) + .withStat(split.getProperty(STATISTIC_FIELD))) + .withId(METRIC_ID); + + dataRequest.withMetricDataQueries(Collections.singletonList(mds)); + + ValueSet timeConstraint = readRecordsRequest.getConstraints().getSummary().get(TIMESTAMP_FIELD); + if (timeConstraint instanceof SortedRangeSet && !timeConstraint.isNullAllowed()) { + //SortedRangeSet is how >, <, between is represented which are easiest and most common when + //searching logs so we attempt to push that down here as an optimization. SQL can represent complex + //overlapping ranges which Cloudwatch can not support so this is not a replacement for applying + //constraints using the ConstraintEvaluator. + + Range basicPredicate = ((SortedRangeSet) timeConstraint).getSpan(); + + if (!basicPredicate.getLow().isNullValue()) { + Long lowerBound = (Long) basicPredicate.getLow().getValue(); + //TODO: confirm timezone handling + logger.info("makeGetMetricsRequest: with startTime " + (lowerBound * 1000) + " " + new Date(lowerBound * 1000)); + dataRequest.withStartTime(new Date(lowerBound * 1000)); + } + else { + //TODO: confirm timezone handling + dataRequest.withStartTime(new Date(0)); + } + + if (!basicPredicate.getHigh().isNullValue()) { + Long upperBound = (Long) basicPredicate.getHigh().getValue(); + //TODO: confirm timezone handling + logger.info("makeGetMetricsRequest: with endTime " + (upperBound * 1000) + " " + new Date(upperBound * 1000)); + dataRequest.withEndTime(new Date(upperBound * 1000)); + } + else { + //TODO: confirm timezone handling + dataRequest.withEndTime(new Date(System.currentTimeMillis())); + } + } + else { + //TODO: confirm timezone handling + dataRequest.withStartTime(new Date(0)); + dataRequest.withEndTime(new Date(System.currentTimeMillis())); + } + + return dataRequest; + } +} diff --git a/athena-cloudwatch-metrics/src/main/java/com/amazonaws/athena/connectors/cloudwatch/metrics/MetricsCompositeHandler.java b/athena-cloudwatch-metrics/src/main/java/com/amazonaws/athena/connectors/cloudwatch/metrics/MetricsCompositeHandler.java new file mode 100644 index 0000000000..6c2999ddf3 --- /dev/null +++ b/athena-cloudwatch-metrics/src/main/java/com/amazonaws/athena/connectors/cloudwatch/metrics/MetricsCompositeHandler.java @@ -0,0 +1,35 @@ +/*- + * #%L + * athena-cloudwatch-metrics + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.cloudwatch.metrics; + +import com.amazonaws.athena.connector.lambda.handlers.CompositeHandler; + +/** + * Boilerplate composite handler that allows us to use a single Lambda function for both + * Metadata and Data. In this case we just compose MetricsMetadataHandler and MetricsRecordHandler. + */ +public class MetricsCompositeHandler + extends CompositeHandler +{ + public MetricsCompositeHandler() + { + super(new MetricsMetadataHandler(), new MetricsRecordHandler()); + } +} diff --git a/athena-cloudwatch-metrics/src/main/java/com/amazonaws/athena/connectors/cloudwatch/metrics/MetricsExceptionFilter.java b/athena-cloudwatch-metrics/src/main/java/com/amazonaws/athena/connectors/cloudwatch/metrics/MetricsExceptionFilter.java new file mode 100644 index 0000000000..4810c6a017 --- /dev/null +++ b/athena-cloudwatch-metrics/src/main/java/com/amazonaws/athena/connectors/cloudwatch/metrics/MetricsExceptionFilter.java @@ -0,0 +1,49 @@ +/*- + * #%L + * athena-cloudwatch-metrics + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.cloudwatch.metrics; + +import com.amazonaws.athena.connector.lambda.ThrottlingInvoker; +import com.amazonaws.services.cloudwatch.model.AmazonCloudWatchException; +import com.amazonaws.services.cloudwatch.model.LimitExceededException; + +/** + * Used to identify Exceptions that are related to Cloudwatch Metrics throttling events. + */ +public class MetricsExceptionFilter + implements ThrottlingInvoker.ExceptionFilter +{ + public static final ThrottlingInvoker.ExceptionFilter EXCEPTION_FILTER = new MetricsExceptionFilter(); + + private MetricsExceptionFilter() {} + + @Override + public boolean isMatch(Exception ex) + { + if (ex instanceof AmazonCloudWatchException && ex.getMessage().startsWith("Rate exceeded")) { + return true; + } + + if (ex instanceof AmazonCloudWatchException && ex.getMessage().startsWith("Request has been throttled")) { + return true; + } + + return (ex instanceof LimitExceededException); + } +} diff --git a/athena-cloudwatch-metrics/src/main/java/com/amazonaws/athena/connectors/cloudwatch/metrics/MetricsMetadataHandler.java b/athena-cloudwatch-metrics/src/main/java/com/amazonaws/athena/connectors/cloudwatch/metrics/MetricsMetadataHandler.java new file mode 100644 index 0000000000..fa8e4dc7b0 --- /dev/null +++ b/athena-cloudwatch-metrics/src/main/java/com/amazonaws/athena/connectors/cloudwatch/metrics/MetricsMetadataHandler.java @@ -0,0 +1,286 @@ +/*- + * #%L + * athena-cloudwatch-metrics + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.cloudwatch.metrics; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.ThrottlingInvoker; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockWriter; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.ConstraintEvaluator; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import com.amazonaws.athena.connector.lambda.handlers.MetadataHandler; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesResponse; +import com.amazonaws.athena.connector.lambda.security.EncryptionKeyFactory; +import com.amazonaws.athena.connectors.cloudwatch.metrics.tables.MetricSamplesTable; +import com.amazonaws.athena.connectors.cloudwatch.metrics.tables.MetricsTable; +import com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.cloudwatch.AmazonCloudWatch; +import com.amazonaws.services.cloudwatch.AmazonCloudWatchClientBuilder; +import com.amazonaws.services.cloudwatch.model.ListMetricsRequest; +import com.amazonaws.services.cloudwatch.model.ListMetricsResult; +import com.amazonaws.services.cloudwatch.model.Metric; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import org.apache.arrow.util.VisibleForTesting; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static com.amazonaws.athena.connectors.cloudwatch.metrics.MetricsExceptionFilter.EXCEPTION_FILTER; +import static com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table.METRIC_NAME_FIELD; +import static com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table.NAMESPACE_FIELD; +import static com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table.PERIOD_FIELD; +import static com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table.STATISTIC_FIELD; + +/** + * Handles metadata requests for the Athena Cloudwatch Metrics Connector. + *

+ * For more detail, please see the module's README.md, some notable characteristics of this class include: + *

+ * 1. Provides two tables (metrics and metric_samples) for accessing Cloudwatch Metrics data via the "default" schema. + * 2. Supports Predicate Pushdown into Cloudwatch Metrics for most fields. + * 3. If multiple Metrics (namespace, metric, dimension(s), and statistic) are requested, they can be read in parallel. + */ +public class MetricsMetadataHandler + extends MetadataHandler +{ + private static final Logger logger = LoggerFactory.getLogger(MetricsMetadataHandler.class); + + //Used to log diagnostic info about this connector + private static final String SOURCE_TYPE = "metrics"; + + //List of available statistics (AVERAGE, p90, etc...). + protected static final List STATISTICS = new ArrayList<>(); + //The schema (aka database) supported by this connector + protected static final String SCHEMA_NAME = "default"; + //Schema for the metrics table + private static final Table METRIC_TABLE; + //Schema for the metric_samples table. + private static final Table METRIC_DATA_TABLE; + //Name of the table which contains details of available metrics. + private static final String METRIC_TABLE_NAME; + //Name of the table which contains metric samples. + private static final String METRIC_SAMPLES_TABLE_NAME; + //Lookup table for resolving table name to Schema. + private static final Map TABLES = new HashMap<>(); + //The default metric period to query (60 seconds) + private static final int DEFAULT_PERIOD_SEC = 60; + //Used to handle throttling events by applying AIMD congestion control + private final ThrottlingInvoker invoker = ThrottlingInvoker.newDefaultBuilder(EXCEPTION_FILTER).build(); + + private final AmazonCloudWatch metrics; + + static { + //The statistics supported by Cloudwatch Metrics by default + STATISTICS.add("Average"); + STATISTICS.add("Minimum"); + STATISTICS.add("Maximum"); + STATISTICS.add("Sum"); + STATISTICS.add("Sample Count"); + STATISTICS.add("p99"); + STATISTICS.add("p95"); + STATISTICS.add("p90"); + STATISTICS.add("p50"); + STATISTICS.add("p10"); + + METRIC_TABLE = new MetricsTable(); + METRIC_DATA_TABLE = new MetricSamplesTable(); + METRIC_TABLE_NAME = METRIC_TABLE.getName(); + METRIC_SAMPLES_TABLE_NAME = METRIC_DATA_TABLE.getName(); + TABLES.put(METRIC_TABLE_NAME, METRIC_TABLE); + TABLES.put(METRIC_SAMPLES_TABLE_NAME, METRIC_DATA_TABLE); + } + + public MetricsMetadataHandler() + { + super(SOURCE_TYPE); + metrics = AmazonCloudWatchClientBuilder.standard().build(); + } + + @VisibleForTesting + protected MetricsMetadataHandler(AmazonCloudWatch metrics, + EncryptionKeyFactory keyFactory, + AWSSecretsManager secretsManager, + AmazonAthena athena, + String spillBucket, + String spillPrefix) + { + super(keyFactory, secretsManager, athena, SOURCE_TYPE, spillBucket, spillPrefix); + this.metrics = metrics; + } + + /** + * Only supports a single, static, schema defined by SCHEMA_NAME. + * + * @see MetadataHandler + */ + @Override + public ListSchemasResponse doListSchemaNames(BlockAllocator blockAllocator, ListSchemasRequest listSchemasRequest) + { + return new ListSchemasResponse(listSchemasRequest.getCatalogName(), Collections.singletonList(SCHEMA_NAME)); + } + + /** + * Supports a set of static tables defined by: TABLES + * + * @see MetadataHandler + */ + @Override + public ListTablesResponse doListTables(BlockAllocator blockAllocator, ListTablesRequest listTablesRequest) + { + List tables = new ArrayList<>(); + TABLES.keySet().stream().forEach(next -> tables.add(new TableName(SCHEMA_NAME, next))); + return new ListTablesResponse(listTablesRequest.getCatalogName(), tables); + } + + /** + * Returns the details of the requested static table. + * + * @see MetadataHandler + */ + @Override + public GetTableResponse doGetTable(BlockAllocator blockAllocator, GetTableRequest getTableRequest) + { + validateTable(getTableRequest.getTableName()); + Table table = TABLES.get(getTableRequest.getTableName().getTableName()); + return new GetTableResponse(getTableRequest.getCatalogName(), + getTableRequest.getTableName(), + table.getSchema(), + table.getPartitionColumns()); + } + + /** + * Our table doesn't support complex layouts or partitioning so we simply make this method a NoOp and the SDK will + * automatically generate a single placeholder partition for us since Athena needs at least 1 partition returned + * if there is potetnailly any data to read. We do this because Cloudwatch Metric's APIs do not support the kind of filtering we need to do + * reasonably scoped partition pruning. Instead we do the pruning at Split generation time and return a single + * partition here. The down side to doing it at Split generation time is that we sacrifice parallelizing Split + * generation. However this is not a significant performance detrement to this connector since we can + * generate Splits rather quickly and easily. + * + * @see MetadataHandler + */ + @Override + public void getPartitions(BlockWriter blockWriter, GetTableLayoutRequest request, QueryStatusChecker queryStatusChecker) + throws Exception + { + validateTable(request.getTableName()); + //NoOp as we do not support partitioning. + } + + /** + * Each 'metric' in cloudwatch is uniquely identified by a quad of Namespace, List, MetricName, Statistic. As such + * we can parallelize each metric as a unique split. + * + * @see MetadataHandler + */ + @Override + public GetSplitsResponse doGetSplits(BlockAllocator blockAllocator, GetSplitsRequest getSplitsRequest) + throws Exception + { + validateTable(getSplitsRequest.getTableName()); + + //Handle requests for the METRIC_TABLE which requires only 1 split to list available metrics. + if (METRIC_TABLE_NAME.equals(getSplitsRequest.getTableName().getTableName())) { + //The request is just for meta-data about what metrics exist. + Split metricsSplit = Split.newBuilder(makeSpillLocation(getSplitsRequest), makeEncryptionKey()).build(); + return new GetSplitsResponse(getSplitsRequest.getCatalogName(), metricsSplit); + } + + //handle generating splits for reading actual metrics data. + try (ConstraintEvaluator constraintEvaluator = new ConstraintEvaluator(blockAllocator, + METRIC_DATA_TABLE.getSchema(), + getSplitsRequest.getConstraints())) { + ListMetricsRequest listMetricsRequest = new ListMetricsRequest(); + MetricUtils.pushDownPredicate(getSplitsRequest.getConstraints(), listMetricsRequest); + listMetricsRequest.setNextToken(getSplitsRequest.getContinuationToken()); + + String period = getPeriodFromConstraint(getSplitsRequest.getConstraints()); + Set splits = new HashSet<>(); + ListMetricsResult result = invoker.invoke(() -> metrics.listMetrics(listMetricsRequest)); + for (Metric nextMetric : result.getMetrics()) { + for (String nextStatistic : STATISTICS) { + if (MetricUtils.applyMetricConstraints(constraintEvaluator, nextMetric, nextStatistic)) { + splits.add(Split.newBuilder(makeSpillLocation(getSplitsRequest), makeEncryptionKey()) + .add(DimensionSerDe.SERIALZIE_DIM_FIELD_NAME, DimensionSerDe.serialize(nextMetric.getDimensions())) + .add(METRIC_NAME_FIELD, nextMetric.getMetricName()) + .add(NAMESPACE_FIELD, nextMetric.getNamespace()) + .add(STATISTIC_FIELD, nextStatistic) + .add(PERIOD_FIELD, period) + .build()); + } + } + } + + String continuationToken = null; + if (result.getNextToken() != null && + !result.getNextToken().equalsIgnoreCase(listMetricsRequest.getNextToken())) { + continuationToken = result.getNextToken(); + } + + return new GetSplitsResponse(getSplitsRequest.getCatalogName(), splits, continuationToken); + } + } + + /** + * Resolved the metric period to query, using a default if no period constraint is found. + */ + private String getPeriodFromConstraint(Constraints constraints) + { + ValueSet period = constraints.getSummary().get(PERIOD_FIELD); + if (period != null && period.isSingleValue()) { + return String.valueOf(period.getSingleValue()); + } + + return String.valueOf(DEFAULT_PERIOD_SEC); + } + + /** + * Validates that the requested schema and table exist in our static set of supported tables. + */ + private void validateTable(TableName tableName) + { + if (!SCHEMA_NAME.equals(tableName.getSchemaName())) { + throw new RuntimeException("Unknown table " + tableName); + } + + if (TABLES.get(tableName.getTableName()) == null) { + throw new RuntimeException("Unknown table " + tableName); + } + } +} diff --git a/athena-cloudwatch-metrics/src/main/java/com/amazonaws/athena/connectors/cloudwatch/metrics/MetricsRecordHandler.java b/athena-cloudwatch-metrics/src/main/java/com/amazonaws/athena/connectors/cloudwatch/metrics/MetricsRecordHandler.java new file mode 100644 index 0000000000..73434dbda6 --- /dev/null +++ b/athena-cloudwatch-metrics/src/main/java/com/amazonaws/athena/connectors/cloudwatch/metrics/MetricsRecordHandler.java @@ -0,0 +1,262 @@ +/*- + * #%L + * athena-cloudwatch-metrics + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.cloudwatch.metrics; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.ThrottlingInvoker; +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockSpiller; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import com.amazonaws.athena.connector.lambda.handlers.RecordHandler; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.athena.connectors.cloudwatch.metrics.tables.MetricSamplesTable; +import com.amazonaws.athena.connectors.cloudwatch.metrics.tables.MetricsTable; +import com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.athena.AmazonAthenaClientBuilder; +import com.amazonaws.services.cloudwatch.AmazonCloudWatch; +import com.amazonaws.services.cloudwatch.AmazonCloudWatchClientBuilder; +import com.amazonaws.services.cloudwatch.model.Dimension; +import com.amazonaws.services.cloudwatch.model.GetMetricDataRequest; +import com.amazonaws.services.cloudwatch.model.GetMetricDataResult; +import com.amazonaws.services.cloudwatch.model.ListMetricsRequest; +import com.amazonaws.services.cloudwatch.model.ListMetricsResult; +import com.amazonaws.services.cloudwatch.model.Metric; +import com.amazonaws.services.cloudwatch.model.MetricDataResult; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.amazonaws.services.secretsmanager.AWSSecretsManagerClientBuilder; +import org.apache.arrow.util.VisibleForTesting; +import org.apache.arrow.vector.types.pojo.Field; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.TimeoutException; + +import static com.amazonaws.athena.connector.lambda.data.FieldResolver.DEFAULT; +import static com.amazonaws.athena.connectors.cloudwatch.metrics.MetricsExceptionFilter.EXCEPTION_FILTER; +import static com.amazonaws.athena.connectors.cloudwatch.metrics.MetricsMetadataHandler.STATISTICS; +import static com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table.DIMENSIONS_FIELD; +import static com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table.DIMENSION_NAME_FIELD; +import static com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table.DIMENSION_VALUE_FIELD; +import static com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table.METRIC_NAME_FIELD; +import static com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table.NAMESPACE_FIELD; +import static com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table.PERIOD_FIELD; +import static com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table.STATISTIC_FIELD; +import static com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table.TIMESTAMP_FIELD; +import static com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table.VALUE_FIELD; + +/** + * Handles data read record requests for the Athena Cloudwatch Metrics Connector. + *

+ * For more detail, please see the module's README.md, some notable characteristics of this class include: + *

+ * 1. Reads and maps Cloudwatch Metrics and Metric Samples. + * 2. Attempts to push down time range predicates into Cloudwatch Metrics. + */ +public class MetricsRecordHandler + extends RecordHandler +{ + private static final Logger logger = LoggerFactory.getLogger(MetricsRecordHandler.class); + + //Used to log diagnostic info about this connector + private static final String SOURCE_TYPE = "metrics"; + //Schema for the metrics table. + private static final Table METRIC_TABLE = new MetricsTable(); + //Schema for the metric_samples table. + private static final Table METRIC_DATA_TABLE = new MetricSamplesTable(); + + //Used to handle throttling events by applying AIMD congestion control + private final ThrottlingInvoker invoker = ThrottlingInvoker.newDefaultBuilder(EXCEPTION_FILTER).build(); + private final AmazonS3 amazonS3; + private final AmazonCloudWatch metrics; + + public MetricsRecordHandler() + { + this(AmazonS3ClientBuilder.defaultClient(), + AWSSecretsManagerClientBuilder.defaultClient(), + AmazonAthenaClientBuilder.defaultClient(), + AmazonCloudWatchClientBuilder.standard().build()); + } + + @VisibleForTesting + protected MetricsRecordHandler(AmazonS3 amazonS3, AWSSecretsManager secretsManager, AmazonAthena athena, AmazonCloudWatch metrics) + { + super(amazonS3, secretsManager, athena, SOURCE_TYPE); + this.amazonS3 = amazonS3; + this.metrics = metrics; + } + + /** + * Scans Cloudwatch Metrics for the list of available metrics or the samples for a specific metric. + * + * @see RecordHandler + */ + @Override + protected void readWithConstraint(BlockSpiller blockSpiller, ReadRecordsRequest readRecordsRequest, QueryStatusChecker queryStatusChecker) + throws TimeoutException + { + invoker.setBlockSpiller(blockSpiller); + if (readRecordsRequest.getTableName().getTableName().equalsIgnoreCase(METRIC_TABLE.getName())) { + readMetricsWithConstraint(blockSpiller, readRecordsRequest, queryStatusChecker); + } + else if (readRecordsRequest.getTableName().getTableName().equalsIgnoreCase(METRIC_DATA_TABLE.getName())) { + readMetricSamplesWithConstraint(blockSpiller, readRecordsRequest, queryStatusChecker); + } + } + + /** + * Handles retrieving the list of available metrics when the METRICS_TABLE is queried by listing metrics in Cloudwatch Metrics. + */ + private void readMetricsWithConstraint(BlockSpiller blockSpiller, ReadRecordsRequest request, QueryStatusChecker queryStatusChecker) + throws TimeoutException + { + ListMetricsRequest listMetricsRequest = new ListMetricsRequest(); + MetricUtils.pushDownPredicate(request.getConstraints(), listMetricsRequest); + String prevToken; + Set requiredFields = new HashSet<>(); + request.getSchema().getFields().stream().forEach(next -> requiredFields.add(next.getName())); + ValueSet dimensionNameConstraint = request.getConstraints().getSummary().get(DIMENSION_NAME_FIELD); + ValueSet dimensionValueConstraint = request.getConstraints().getSummary().get(DIMENSION_VALUE_FIELD); + do { + prevToken = listMetricsRequest.getNextToken(); + ListMetricsResult result = invoker.invoke(() -> metrics.listMetrics(listMetricsRequest)); + for (Metric nextMetric : result.getMetrics()) { + blockSpiller.writeRows((Block block, int row) -> { + boolean matches = MetricUtils.applyMetricConstraints(blockSpiller.getConstraintEvaluator(), nextMetric, null); + if (matches) { + matches &= block.offerValue(METRIC_NAME_FIELD, row, nextMetric.getMetricName()); + matches &= block.offerValue(NAMESPACE_FIELD, row, nextMetric.getNamespace()); + matches &= block.offerComplexValue(STATISTIC_FIELD, row, DEFAULT, STATISTICS); + + matches &= block.offerComplexValue(DIMENSIONS_FIELD, + row, + (Field field, Object val) -> { + if (field.getName().equals(DIMENSION_NAME_FIELD)) { + return ((Dimension) val).getName(); + } + else if (field.getName().equals(DIMENSION_VALUE_FIELD)) { + return ((Dimension) val).getValue(); + } + + throw new RuntimeException("Unexpected field " + field.getName()); + }, + nextMetric.getDimensions()); + + //This field is 'faked' in that we just use it as a convenient way to filter single dimensions. As such + //we always populate it with the value of the filter if the constraint passed and the filter was singleValue + String dimName = (dimensionNameConstraint == null || !dimensionNameConstraint.isSingleValue()) + ? null : (dimensionNameConstraint.getSingleValue().toString()); + matches &= block.offerValue(DIMENSION_NAME_FIELD, row, dimName); + + //This field is 'faked' in that we just use it as a convenient way to filter single dimensions. As such + //we always populate it with the value of the filter if the constraint passed and the filter was singleValue + String dimValue = (dimensionValueConstraint == null || !dimensionValueConstraint.isSingleValue()) + ? null : dimensionValueConstraint.getSingleValue().toString(); + matches &= block.offerValue(DIMENSION_VALUE_FIELD, row, dimValue); + } + return matches ? 1 : 0; + }); + } + listMetricsRequest.setNextToken(result.getNextToken()); + } + while (listMetricsRequest.getNextToken() != null && !listMetricsRequest.getNextToken().equalsIgnoreCase(prevToken) && queryStatusChecker.isQueryRunning()); + } + + /** + * Handles retrieving the samples for a specific metric from Cloudwatch Metrics. + */ + private void readMetricSamplesWithConstraint(BlockSpiller blockSpiller, ReadRecordsRequest request, QueryStatusChecker queryStatusChecker) + throws TimeoutException + { + Split split = request.getSplit(); + List dimensions = DimensionSerDe.deserialize(split.getProperty(DimensionSerDe.SERIALZIE_DIM_FIELD_NAME)); + GetMetricDataRequest dataRequest = MetricUtils.makeGetMetricDataRequest(request); + + String prevToken; + Set requiredFields = new HashSet<>(); + request.getSchema().getFields().stream().forEach(next -> requiredFields.add(next.getName())); + ValueSet dimensionNameConstraint = request.getConstraints().getSummary().get(DIMENSION_NAME_FIELD); + ValueSet dimensionValueConstraint = request.getConstraints().getSummary().get(DIMENSION_NAME_FIELD); + do { + prevToken = dataRequest.getNextToken(); + GetMetricDataResult result = invoker.invoke(() -> metrics.getMetricData(dataRequest)); + for (MetricDataResult nextMetric : result.getMetricDataResults()) { + List timestamps = nextMetric.getTimestamps(); + List values = nextMetric.getValues(); + for (int i = 0; i < nextMetric.getValues().size(); i++) { + int sampleNum = i; + blockSpiller.writeRows((Block block, int row) -> { + /** + * Most constraints were already applied at split generation so we only need to apply + * a subset. + */ + block.offerValue(METRIC_NAME_FIELD, row, split.getProperty(METRIC_NAME_FIELD)); + block.offerValue(NAMESPACE_FIELD, row, split.getProperty(NAMESPACE_FIELD)); + block.offerValue(STATISTIC_FIELD, row, split.getProperty(STATISTIC_FIELD)); + + block.offerComplexValue(DIMENSIONS_FIELD, + row, + (Field field, Object val) -> { + if (field.getName().equals(DIMENSION_NAME_FIELD)) { + return ((Dimension) val).getName(); + } + else if (field.getName().equals(DIMENSION_VALUE_FIELD)) { + return ((Dimension) val).getValue(); + } + + throw new RuntimeException("Unexpected field " + field.getName()); + }, + dimensions); + + //This field is 'faked' in that we just use it as a convenient way to filter single dimensions. As such + //we always populate it with the value of the filter if the constraint passed and the filter was singleValue + String dimName = (dimensionNameConstraint == null || !dimensionNameConstraint.isSingleValue()) + ? null : dimensionNameConstraint.getSingleValue().toString(); + block.offerValue(DIMENSION_NAME_FIELD, row, dimName); + + //This field is 'faked' in that we just use it as a convenient way to filter single dimensions. As such + //we always populate it with the value of the filter if the constraint passed and the filter was singleValue + String dimVal = (dimensionValueConstraint == null || !dimensionValueConstraint.isSingleValue()) + ? null : dimensionValueConstraint.getSingleValue().toString(); + block.offerValue(DIMENSION_VALUE_FIELD, row, dimVal); + + block.offerValue(PERIOD_FIELD, row, Integer.valueOf(split.getProperty(PERIOD_FIELD))); + + boolean matches = true; + block.offerValue(VALUE_FIELD, row, values.get(sampleNum)); + long timestamp = timestamps.get(sampleNum).getTime() / 1000; + block.offerValue(TIMESTAMP_FIELD, row, timestamp); + + return matches ? 1 : 0; + }); + } + } + dataRequest.setNextToken(result.getNextToken()); + } + while (dataRequest.getNextToken() != null && !dataRequest.getNextToken().equalsIgnoreCase(prevToken) && queryStatusChecker.isQueryRunning()); + } +} diff --git a/athena-cloudwatch-metrics/src/main/java/com/amazonaws/athena/connectors/cloudwatch/metrics/tables/MetricSamplesTable.java b/athena-cloudwatch-metrics/src/main/java/com/amazonaws/athena/connectors/cloudwatch/metrics/tables/MetricSamplesTable.java new file mode 100644 index 0000000000..02f325a9ac --- /dev/null +++ b/athena-cloudwatch-metrics/src/main/java/com/amazonaws/athena/connectors/cloudwatch/metrics/tables/MetricSamplesTable.java @@ -0,0 +1,100 @@ +/*- + * #%L + * athena-cloudwatch-metrics + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.cloudwatch.metrics.tables; + +import com.amazonaws.athena.connector.lambda.data.FieldBuilder; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Schema; + +import java.util.Collections; +import java.util.Set; + +/** + * Defines the metadata associated with our static metric_samples table. + *

+ * This table contains the available metric samples for each metric named in the **metrics** table. + * More specifically, the table contains the following columns: + *

+ * **namespace** - A VARCHAR containing the namespace. + * **metric_name** - A VARCHAR containing the metric name. + * **dimensions** - A LIST of STRUCTS comprised of dim_name (VARCHAR) and dim_value (VARCHAR). + * **dim_name** - A VARCHAR convenience field used to easily filter on a single dimension name. + * **dim_value** - A VARCHAR convenience field used to easily filter on a single dimension value. + * **period** - An INT field representing the 'period' of the metric in seconds. (e.g. 60 second metric) + * **timestamp** - A BIGINT field representing the epoch time (in seconds) the metric sample is for. + * **value** - A FLOAT8 field containing the value of the sample. + * **statistic** - A VARCHAR containing the statistic type of the sample. (e.g. AVERAGE, p90, etc..) + */ +public class MetricSamplesTable + extends Table +{ + private final Schema schema; + private final String name; + + public MetricSamplesTable() + { + schema = new SchemaBuilder().newBuilder() + .addStringField(NAMESPACE_FIELD) + .addStringField(METRIC_NAME_FIELD) + .addField(FieldBuilder.newBuilder(DIMENSIONS_FIELD, Types.MinorType.LIST.getType()) + .addField(FieldBuilder.newBuilder(DIMENSIONS_FIELD, Types.MinorType.STRUCT.getType()) + .addStringField(DIMENSION_NAME_FIELD) + .addStringField(DIMENSION_VALUE_FIELD) + .build()) + .build()) + .addStringField(DIMENSION_NAME_FIELD) + .addStringField(DIMENSION_VALUE_FIELD) + .addIntField(PERIOD_FIELD) + .addBigIntField(TIMESTAMP_FIELD) + .addFloat8Field(VALUE_FIELD) + .addStringField(STATISTIC_FIELD) + .addMetadata(NAMESPACE_FIELD, "Metric namespace") + .addMetadata(METRIC_NAME_FIELD, "Metric name") + .addMetadata(DIMENSIONS_FIELD, "Array of Dimensions for the given metric.") + .addMetadata(DIMENSION_NAME_FIELD, "Shortcut field that flattens dimension to allow easier filtering on a single dimension name. This field is left blank unless used in the where clause") + .addMetadata(DIMENSION_VALUE_FIELD, "Shortcut field that flattens dimension to allow easier filtering on a single dimension value. This field is left blank unless used in the where clause.") + .addMetadata(STATISTIC_FIELD, "Statistics type of this value (e.g. Maximum, Minimum, Average, Sample Count)") + .addMetadata(TIMESTAMP_FIELD, "The epoch time (in seconds) the value is for.") + .addMetadata(PERIOD_FIELD, "The period, in seconds, for the metric (e.g. 60 seconds, 120 seconds)") + .addMetadata(VALUE_FIELD, "The value for the sample.") + .build(); + + name = "metric_samples"; + } + + @Override + public String getName() + { + return name; + } + + @Override + public Schema getSchema() + { + return schema; + } + + @Override + public Set getPartitionColumns() + { + return Collections.emptySet(); + } +} diff --git a/athena-cloudwatch-metrics/src/main/java/com/amazonaws/athena/connectors/cloudwatch/metrics/tables/MetricsTable.java b/athena-cloudwatch-metrics/src/main/java/com/amazonaws/athena/connectors/cloudwatch/metrics/tables/MetricsTable.java new file mode 100644 index 0000000000..6c76356e38 --- /dev/null +++ b/athena-cloudwatch-metrics/src/main/java/com/amazonaws/athena/connectors/cloudwatch/metrics/tables/MetricsTable.java @@ -0,0 +1,88 @@ +/*- + * #%L + * athena-cloudwatch-metrics + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.cloudwatch.metrics.tables; + +import com.amazonaws.athena.connector.lambda.data.FieldBuilder; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Schema; + +import java.util.Collections; +import java.util.Set; + +/** + * Defines the metadata associated with our static metrics table. + *

+ * This table contains the available metrics as uniquely defined by a triple of namespace, set, name. + * More specifically, this table contains the following columns. + * * **namespace** - A VARCHAR containing the namespace. + * * **metric_name** - A VARCHAR containing the metric name. + * * **dimensions** - A LIST of STRUCTS comprised of dim_name (VARCHAR) and dim_value (VARCHAR). + * * **statistic** - A List of VARCH statistics (e.g. p90, AVERAGE, etc..) avialable for the metric. + */ +public class MetricsTable + extends Table +{ + private final Schema schema; + private final String name; + + public MetricsTable() + { + schema = new SchemaBuilder().newBuilder() + .addStringField(NAMESPACE_FIELD) + .addStringField(METRIC_NAME_FIELD) + .addField(FieldBuilder.newBuilder(DIMENSIONS_FIELD, Types.MinorType.LIST.getType()) + .addField(FieldBuilder.newBuilder(DIMENSIONS_FIELD, Types.MinorType.STRUCT.getType()) + .addStringField(DIMENSION_NAME_FIELD) + .addStringField(DIMENSION_VALUE_FIELD) + .build()) + .build()) + .addStringField(DIMENSION_NAME_FIELD) + .addStringField(DIMENSION_VALUE_FIELD) + .addListField(STATISTIC_FIELD, Types.MinorType.VARCHAR.getType()) + .addMetadata(NAMESPACE_FIELD, "Metric namespace") + .addMetadata(METRIC_NAME_FIELD, "Metric name") + .addMetadata(STATISTIC_FIELD, "List of statistics available for this metric (e.g. Maximum, Minimum, Average, Sample Count)") + .addMetadata(DIMENSIONS_FIELD, "Array of Dimensions for the given metric.") + .addMetadata(DIMENSION_NAME_FIELD, "Shortcut field that flattens dimension to allow easier filtering for metrics that contain the dimension name. This field is left blank unless used in the where clause.") + .addMetadata(DIMENSION_VALUE_FIELD, "Shortcut field that flattens dimension to allow easier filtering for metrics that contain the dimension value. This field is left blank unless used in the where clause.") + .build(); + + name = "metrics"; + } + + @Override + public String getName() + { + return name; + } + + @Override + public Schema getSchema() + { + return schema; + } + + @Override + public Set getPartitionColumns() + { + return Collections.emptySet(); + } +} diff --git a/athena-cloudwatch-metrics/src/main/java/com/amazonaws/athena/connectors/cloudwatch/metrics/tables/Table.java b/athena-cloudwatch-metrics/src/main/java/com/amazonaws/athena/connectors/cloudwatch/metrics/tables/Table.java new file mode 100644 index 0000000000..812803d58c --- /dev/null +++ b/athena-cloudwatch-metrics/src/main/java/com/amazonaws/athena/connectors/cloudwatch/metrics/tables/Table.java @@ -0,0 +1,53 @@ +/*- + * #%L + * athena-cloudwatch-metrics + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.cloudwatch.metrics.tables; + +import org.apache.arrow.vector.types.pojo.Schema; + +import java.util.Set; + +/** + * Defines some commonly required field names used by all tables and consumers of tables in this connector. + */ +public abstract class Table +{ + //The name of the metric name field. + public static final String METRIC_NAME_FIELD = "metric_name"; + //The name of the namespace field. + public static final String NAMESPACE_FIELD = "namespace"; + //The name of the dimensions field which houses a list of Cloudwatch Metrics Dimensions. + public static final String DIMENSIONS_FIELD = "dimensions"; + //The name of the convenience Dimension name field which gives easy access to 1 dimension name. + public static final String DIMENSION_NAME_FIELD = "dim_name"; + //The name of the convenience Dimension value field which gives easy access to 1 dimension value. + public static final String DIMENSION_VALUE_FIELD = "dim_value"; + //The name of the timestamp field, denoting the time period a particular metric sample was for. + public static final String TIMESTAMP_FIELD = "timestamp"; + //The name of the metric value field which holds the value of a metric sample. + public static final String VALUE_FIELD = "value"; + //The name of the statistic field (e.g. AVERAGE, p90). + public static final String STATISTIC_FIELD = "statistic"; + //The name of the period field (e.g. 60 seconds). + public static final String PERIOD_FIELD = "period"; + + public abstract String getName(); + public abstract Schema getSchema(); + public abstract Set getPartitionColumns(); +} diff --git a/athena-cloudwatch-metrics/src/test/java/com/amazonaws/athena/connectors/cloudwatch/metrics/DimensionSerDeTest.java b/athena-cloudwatch-metrics/src/test/java/com/amazonaws/athena/connectors/cloudwatch/metrics/DimensionSerDeTest.java new file mode 100644 index 0000000000..279bd7196b --- /dev/null +++ b/athena-cloudwatch-metrics/src/test/java/com/amazonaws/athena/connectors/cloudwatch/metrics/DimensionSerDeTest.java @@ -0,0 +1,51 @@ +/*- + * #%L + * athena-cloudwatch-metrics + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.cloudwatch.metrics; + +import com.amazonaws.services.cloudwatch.model.Dimension; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.*; + +public class DimensionSerDeTest +{ + private static final Logger logger = LoggerFactory.getLogger(DimensionSerDeTest.class); + private static final String EXPECTED_SERIALIZATION = "{\"dimensions\":[{\"name\":\"dim_name\",\"value\":\"dim_val\"}" + + ",{\"name\":\"dim_name1\",\"value\":\"dim_val1\"},{\"name\":\"dim_name2\",\"value\":\"dim_val2\"}]}"; + + @Test + public void serializeTest() + { + List expected = new ArrayList<>(); + expected.add(new Dimension().withName("dim_name").withValue("dim_val")); + expected.add(new Dimension().withName("dim_name1").withValue("dim_val1")); + expected.add(new Dimension().withName("dim_name2").withValue("dim_val2")); + String actualSerialization = DimensionSerDe.serialize(expected); + logger.info("serializeTest: {}", actualSerialization); + List actual = DimensionSerDe.deserialize(actualSerialization); + assertEquals(EXPECTED_SERIALIZATION, actualSerialization); + assertEquals(expected, actual); + } +} diff --git a/athena-cloudwatch-metrics/src/test/java/com/amazonaws/athena/connectors/cloudwatch/metrics/MetricUtilsTest.java b/athena-cloudwatch-metrics/src/test/java/com/amazonaws/athena/connectors/cloudwatch/metrics/MetricUtilsTest.java new file mode 100644 index 0000000000..dbd839fac1 --- /dev/null +++ b/athena-cloudwatch-metrics/src/test/java/com/amazonaws/athena/connectors/cloudwatch/metrics/MetricUtilsTest.java @@ -0,0 +1,203 @@ +/*- + * #%L + * athena-cloudwatch-metrics + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.cloudwatch.metrics; + +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.ConstraintEvaluator; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.domain.predicate.Range; +import com.amazonaws.athena.connector.lambda.domain.predicate.SortedRangeSet; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.athena.connector.lambda.security.FederatedIdentity; +import com.amazonaws.services.cloudwatch.model.Dimension; +import com.amazonaws.services.cloudwatch.model.DimensionFilter; +import com.amazonaws.services.cloudwatch.model.GetMetricDataRequest; +import com.amazonaws.services.cloudwatch.model.ListMetricsRequest; +import com.amazonaws.services.cloudwatch.model.Metric; +import com.amazonaws.services.cloudwatch.model.MetricStat; +import org.apache.arrow.vector.types.pojo.Schema; +import com.google.common.collect.ImmutableList; +import org.apache.arrow.vector.types.Types; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.amazonaws.athena.connectors.cloudwatch.metrics.DimensionSerDe.SERIALZIE_DIM_FIELD_NAME; +import static com.amazonaws.athena.connectors.cloudwatch.metrics.TestUtils.makeStringEquals; +import static com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table.DIMENSION_NAME_FIELD; +import static com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table.DIMENSION_VALUE_FIELD; +import static com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table.METRIC_NAME_FIELD; +import static com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table.NAMESPACE_FIELD; +import static com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table.PERIOD_FIELD; +import static com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table.STATISTIC_FIELD; +import static com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table.TIMESTAMP_FIELD; +import static org.junit.Assert.*; + +public class MetricUtilsTest +{ + private FederatedIdentity identity = new FederatedIdentity("id", "principal", "account"); + private String catalog = "default"; + private BlockAllocator allocator; + + @Before + public void setup() + { + allocator = new BlockAllocatorImpl(); + } + + @After + public void tearDown() + { + allocator.close(); + } + + @Test + public void applyMetricConstraints() + { + Schema schema = SchemaBuilder.newBuilder() + .addStringField(NAMESPACE_FIELD) + .addStringField(METRIC_NAME_FIELD) + .addStringField(STATISTIC_FIELD) + .addStringField(DIMENSION_NAME_FIELD) + .addStringField(DIMENSION_VALUE_FIELD) + .build(); + + Map constraintsMap = new HashMap<>(); + constraintsMap.put(NAMESPACE_FIELD, makeStringEquals(allocator, "match1")); + constraintsMap.put(METRIC_NAME_FIELD, makeStringEquals(allocator, "match2")); + constraintsMap.put(STATISTIC_FIELD, makeStringEquals(allocator, "match3")); + constraintsMap.put(DIMENSION_NAME_FIELD, makeStringEquals(allocator, "match4")); + constraintsMap.put(DIMENSION_VALUE_FIELD, makeStringEquals(allocator, "match5")); + + ConstraintEvaluator constraintEvaluator = new ConstraintEvaluator(allocator, schema, new Constraints(constraintsMap)); + + Metric metric = new Metric() + .withNamespace("match1") + .withMetricName("match2") + .withDimensions(new Dimension().withName("match4").withValue("match5")); + String statistic = "match3"; + assertTrue(MetricUtils.applyMetricConstraints(constraintEvaluator, metric, statistic)); + + assertFalse(MetricUtils.applyMetricConstraints(constraintEvaluator, copyMetric(metric).withNamespace("no_match"), statistic)); + assertFalse(MetricUtils.applyMetricConstraints(constraintEvaluator, copyMetric(metric).withMetricName("no_match"), statistic)); + assertFalse(MetricUtils.applyMetricConstraints(constraintEvaluator, + copyMetric(metric).withDimensions(Collections.singletonList(new Dimension().withName("no_match").withValue("match5"))), statistic)); + assertFalse(MetricUtils.applyMetricConstraints(constraintEvaluator, + copyMetric(metric).withDimensions(Collections.singletonList(new Dimension().withName("match4").withValue("no_match"))), statistic)); + assertFalse(MetricUtils.applyMetricConstraints(constraintEvaluator, copyMetric(metric), "no_match")); + } + + private Metric copyMetric(Metric metric) + { + Metric newMetric = new Metric() + .withNamespace(metric.getNamespace()) + .withMetricName(metric.getMetricName()); + + List dims = new ArrayList<>(); + for (Dimension next : metric.getDimensions()) { + dims.add(new Dimension().withName(next.getName()).withValue(next.getValue())); + } + return newMetric.withDimensions(dims); + } + + @Test + public void pushDownPredicate() + { + Map constraintsMap = new HashMap<>(); + constraintsMap.put(NAMESPACE_FIELD, makeStringEquals(allocator, "match1")); + constraintsMap.put(METRIC_NAME_FIELD, makeStringEquals(allocator, "match2")); + constraintsMap.put(STATISTIC_FIELD, makeStringEquals(allocator, "match3")); + constraintsMap.put(DIMENSION_NAME_FIELD, makeStringEquals(allocator, "match4")); + constraintsMap.put(DIMENSION_VALUE_FIELD, makeStringEquals(allocator, "match5")); + + ListMetricsRequest request = new ListMetricsRequest(); + MetricUtils.pushDownPredicate(new Constraints(constraintsMap), request); + + assertEquals("match1", request.getNamespace()); + assertEquals("match2", request.getMetricName()); + assertEquals(1, request.getDimensions().size()); + assertEquals(new DimensionFilter().withName("match4").withValue("match5"), request.getDimensions().get(0)); + } + + @Test + public void makeGetMetricDataRequest() + { + String schema = "schema"; + String table = "table"; + Integer period = 60; + String statistic = "p90"; + String metricName = "metricName"; + String namespace = "namespace"; + + List dimensions = new ArrayList<>(); + dimensions.add(new Dimension().withName("dim_name1").withValue("dim_value1")); + dimensions.add(new Dimension().withName("dim_name2").withValue("dim_value2")); + + Split split = Split.newBuilder(null, null) + .add(NAMESPACE_FIELD, namespace) + .add(METRIC_NAME_FIELD, metricName) + .add(PERIOD_FIELD, String.valueOf(period)) + .add(STATISTIC_FIELD, statistic) + .add(SERIALZIE_DIM_FIELD_NAME, DimensionSerDe.serialize(dimensions)) + .build(); + + Schema schemaForRead = SchemaBuilder.newBuilder().addStringField(METRIC_NAME_FIELD).build(); + + Map constraintsMap = new HashMap<>(); + + constraintsMap.put(TIMESTAMP_FIELD, SortedRangeSet.copyOf(Types.MinorType.BIGINT.getType(), + ImmutableList.of(Range.greaterThan(allocator, Types.MinorType.BIGINT.getType(), 1L)), false)); + + ReadRecordsRequest request = new ReadRecordsRequest(identity, + catalog, + "queryId-" + System.currentTimeMillis(), + new TableName(schema, table), + schemaForRead, + split, + new Constraints(constraintsMap), + 100_000_000_000L, //100GB don't expect this to spill + 100_000_000_000L + ); + + GetMetricDataRequest actual = MetricUtils.makeGetMetricDataRequest(request); + assertEquals(1, actual.getMetricDataQueries().size()); + assertNotNull(actual.getMetricDataQueries().get(0).getId()); + MetricStat metricStat = actual.getMetricDataQueries().get(0).getMetricStat(); + assertNotNull(metricStat); + assertEquals(metricName, metricStat.getMetric().getMetricName()); + assertEquals(namespace, metricStat.getMetric().getNamespace()); + assertEquals(statistic, metricStat.getStat()); + assertEquals(period, metricStat.getPeriod()); + assertEquals(2, metricStat.getMetric().getDimensions().size()); + assertEquals(1000L, actual.getStartTime().getTime()); + assertTrue(actual.getStartTime().getTime() <= System.currentTimeMillis() + 1_000); + } +} diff --git a/athena-cloudwatch-metrics/src/test/java/com/amazonaws/athena/connectors/cloudwatch/metrics/MetricsMetadataHandlerTest.java b/athena-cloudwatch-metrics/src/test/java/com/amazonaws/athena/connectors/cloudwatch/metrics/MetricsMetadataHandlerTest.java new file mode 100644 index 0000000000..bbe9719a40 --- /dev/null +++ b/athena-cloudwatch-metrics/src/test/java/com/amazonaws/athena/connectors/cloudwatch/metrics/MetricsMetadataHandlerTest.java @@ -0,0 +1,337 @@ +/*- + * #%L + * athena-cloudwatch-metrics + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.cloudwatch.metrics; + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.amazonaws.athena.connector.lambda.data.BlockUtils; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.domain.predicate.EquatableValueSet; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesResponse; +import com.amazonaws.athena.connector.lambda.metadata.MetadataRequestType; +import com.amazonaws.athena.connector.lambda.metadata.MetadataResponse; +import com.amazonaws.athena.connector.lambda.security.FederatedIdentity; +import com.amazonaws.athena.connector.lambda.security.LocalKeyFactory; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.cloudwatch.AmazonCloudWatch; +import com.amazonaws.services.cloudwatch.model.ListMetricsRequest; +import com.amazonaws.services.cloudwatch.model.ListMetricsResult; +import com.amazonaws.services.cloudwatch.model.Metric; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Schema; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.runners.MockitoJUnitRunner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.amazonaws.athena.connectors.cloudwatch.metrics.DimensionSerDe.SERIALZIE_DIM_FIELD_NAME; +import static com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table.METRIC_NAME_FIELD; +import static com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table.NAMESPACE_FIELD; +import static com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table.PERIOD_FIELD; +import static com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table.STATISTIC_FIELD; +import static org.junit.Assert.*; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class MetricsMetadataHandlerTest +{ + private static final Logger logger = LoggerFactory.getLogger(MetricsMetadataHandlerTest.class); + + private final String defaultSchema = "default"; + private final FederatedIdentity identity = new FederatedIdentity("id", "principal", "account"); + + private MetricsMetadataHandler handler; + private BlockAllocator allocator; + + @Mock + private AmazonCloudWatch mockMetrics; + + @Mock + private AWSSecretsManager mockSecretsManager; + + @Mock + private AmazonAthena mockAthena; + + @Before + public void setUp() + throws Exception + { + handler = new MetricsMetadataHandler(mockMetrics, new LocalKeyFactory(), mockSecretsManager, mockAthena, "spillBucket", "spillPrefix"); + allocator = new BlockAllocatorImpl(); + } + + @After + public void tearDown() + throws Exception + { + allocator.close(); + } + + @Test + public void doListSchemaNames() + { + logger.info("doListSchemas - enter"); + + ListSchemasRequest req = new ListSchemasRequest(identity, "queryId", "default"); + ListSchemasResponse res = handler.doListSchemaNames(allocator, req); + logger.info("doListSchemas - {}", res.getSchemas()); + + assertTrue(res.getSchemas().size() == 1); + assertEquals(defaultSchema, res.getSchemas().iterator().next()); + + logger.info("doListSchemas - exit"); + } + + @Test + public void doListTables() + { + logger.info("doListTables - enter"); + + ListTablesRequest req = new ListTablesRequest(identity, "queryId", "default", defaultSchema); + ListTablesResponse res = handler.doListTables(allocator, req); + logger.info("doListTables - {}", res.getTables()); + + assertEquals(2, res.getTables().size()); + assertTrue(res.getTables().contains(new TableName(defaultSchema, "metrics"))); + assertTrue(res.getTables().contains(new TableName(defaultSchema, "metric_samples"))); + + logger.info("doListTables - exit"); + } + + @Test + public void doGetMetricsTable() + { + logger.info("doGetMetricsTable - enter"); + + GetTableRequest metricsTableReq = new GetTableRequest(identity, "queryId", "default", new TableName(defaultSchema, "metrics")); + GetTableResponse metricsTableRes = handler.doGetTable(allocator, metricsTableReq); + logger.info("doGetMetricsTable - {} {}", metricsTableRes.getTableName(), metricsTableRes.getSchema()); + + assertEquals(new TableName(defaultSchema, "metrics"), metricsTableRes.getTableName()); + assertNotNull(metricsTableRes.getSchema()); + assertEquals(6, metricsTableRes.getSchema().getFields().size()); + + logger.info("doGetMetricsTable - exit"); + } + + @Test + public void doGetMetricSamplesTable() + { + logger.info("doGetMetricSamplesTable - enter"); + + GetTableRequest metricsTableReq = new GetTableRequest(identity, + "queryId", + "default", + new TableName(defaultSchema, "metric_samples")); + + GetTableResponse metricsTableRes = handler.doGetTable(allocator, metricsTableReq); + logger.info("doGetMetricSamplesTable - {} {}", metricsTableRes.getTableName(), metricsTableRes.getSchema()); + + assertEquals(new TableName(defaultSchema, "metric_samples"), metricsTableRes.getTableName()); + assertNotNull(metricsTableRes.getSchema()); + assertEquals(9, metricsTableRes.getSchema().getFields().size()); + + logger.info("doGetMetricSamplesTable - exit"); + } + + @Test + public void doGetTableLayout() + throws Exception + { + logger.info("doGetTableLayout - enter"); + + Map constraintsMap = new HashMap<>(); + + constraintsMap.put(METRIC_NAME_FIELD, + EquatableValueSet.newBuilder(allocator, Types.MinorType.VARCHAR.getType(), true, false) + .add("MyMetric").build()); + + GetTableLayoutRequest req = new GetTableLayoutRequest(identity, + "queryId", + "default", + new TableName(defaultSchema, "metrics"), + new Constraints(constraintsMap), + SchemaBuilder.newBuilder().build(), + Collections.EMPTY_SET); + + GetTableLayoutResponse res = handler.doGetTableLayout(allocator, req); + + logger.info("doGetTableLayout - {}", res.getPartitions().getSchema()); + logger.info("doGetTableLayout - {}", res.getPartitions()); + + assertTrue(res.getPartitions().getRowCount() == 1); + + logger.info("doGetTableLayout - exit"); + } + + @Test + public void doGetMetricsSplits() + throws Exception + { + logger.info("doGetMetricsSplits: enter"); + + Schema schema = SchemaBuilder.newBuilder().addIntField("partitionId").build(); + + Block partitions = allocator.createBlock(schema); + BlockUtils.setValue(partitions.getFieldVector("partitionId"), 1, 1); + partitions.setRowCount(1); + + String continuationToken = null; + GetSplitsRequest originalReq = new GetSplitsRequest(identity, + "queryId", + "catalog_name", + new TableName(defaultSchema, "metrics"), + partitions, + Collections.singletonList("partitionId"), + new Constraints(new HashMap<>()), + continuationToken); + int numContinuations = 0; + do { + GetSplitsRequest req = new GetSplitsRequest(originalReq, continuationToken); + logger.info("doGetMetricsSplits: req[{}]", req); + + MetadataResponse rawResponse = handler.doGetSplits(allocator, req); + assertEquals(MetadataRequestType.GET_SPLITS, rawResponse.getRequestType()); + + GetSplitsResponse response = (GetSplitsResponse) rawResponse; + continuationToken = response.getContinuationToken(); + + logger.info("doGetMetricsSplits: continuationToken[{}] - numSplits[{}]", continuationToken, response.getSplits().size()); + assertEquals(1, response.getSplits().size()); + + if (continuationToken != null) { + numContinuations++; + } + } + while (continuationToken != null); + + assertEquals(0, numContinuations); + + logger.info("doGetMetricsSplits: exit"); + } + + @Test + public void doGetMetricSamplesSplits() + throws Exception + { + logger.info("doGetMetricSamplesSplits: enter"); + + String namespaceFilter = "MyNameSpace"; + String statistic = "p90"; + int numMetrics = 10; + + when(mockMetrics.listMetrics(any(ListMetricsRequest.class))).thenAnswer((InvocationOnMock invocation) -> { + ListMetricsRequest request = invocation.getArgumentAt(0, ListMetricsRequest.class); + + //assert that the namespace filter was indeed pushed down + assertEquals(namespaceFilter, request.getNamespace()); + String nextToken = (request.getNextToken() == null) ? "valid" : null; + List metrics = new ArrayList<>(); + + for (int i = 0; i < numMetrics; i++) { + metrics.add(new Metric().withNamespace(namespaceFilter).withMetricName("metric-" + i)); + } + + return new ListMetricsResult().withNextToken(nextToken).withMetrics(metrics); + }); + + Schema schema = SchemaBuilder.newBuilder().addIntField("partitionId").build(); + + Block partitions = allocator.createBlock(schema); + BlockUtils.setValue(partitions.getFieldVector("partitionId"), 1, 1); + partitions.setRowCount(1); + + Map constraintsMap = new HashMap<>(); + + constraintsMap.put(NAMESPACE_FIELD, + EquatableValueSet.newBuilder(allocator, Types.MinorType.VARCHAR.getType(), true, false) + .add(namespaceFilter).build()); + constraintsMap.put(STATISTIC_FIELD, + EquatableValueSet.newBuilder(allocator, Types.MinorType.VARCHAR.getType(), true, false) + .add(statistic).build()); + + String continuationToken = null; + GetSplitsRequest originalReq = new GetSplitsRequest(identity, + "queryId", + "catalog_name", + new TableName(defaultSchema, "metric_samples"), + partitions, + Collections.singletonList("partitionId"), + new Constraints(constraintsMap), + continuationToken); + + int numContinuations = 0; + do { + GetSplitsRequest req = new GetSplitsRequest(originalReq, continuationToken); + logger.info("doGetMetricSamplesSplits: req[{}]", req); + + MetadataResponse rawResponse = handler.doGetSplits(allocator, req); + assertEquals(MetadataRequestType.GET_SPLITS, rawResponse.getRequestType()); + + GetSplitsResponse response = (GetSplitsResponse) rawResponse; + continuationToken = response.getContinuationToken(); + + logger.info("doGetMetricSamplesSplits: continuationToken[{}] - numSplits[{}]", continuationToken, response.getSplits().size()); + assertEquals(numMetrics, response.getSplits().size()); + for (Split nextSplit : response.getSplits()) { + assertNotNull(nextSplit.getProperty(SERIALZIE_DIM_FIELD_NAME)); + assertNotNull(nextSplit.getProperty(METRIC_NAME_FIELD)); + assertEquals(statistic, nextSplit.getProperty(STATISTIC_FIELD)); + assertEquals("60", nextSplit.getProperty(PERIOD_FIELD)); + } + + if (continuationToken != null) { + numContinuations++; + } + } + while (continuationToken != null); + + assertEquals(1, numContinuations); + + logger.info("doGetMetricSamplesSplits: exit"); + } +} diff --git a/athena-cloudwatch-metrics/src/test/java/com/amazonaws/athena/connectors/cloudwatch/metrics/MetricsRecordHandlerTest.java b/athena-cloudwatch-metrics/src/test/java/com/amazonaws/athena/connectors/cloudwatch/metrics/MetricsRecordHandlerTest.java new file mode 100644 index 0000000000..7ae483d36b --- /dev/null +++ b/athena-cloudwatch-metrics/src/test/java/com/amazonaws/athena/connectors/cloudwatch/metrics/MetricsRecordHandlerTest.java @@ -0,0 +1,343 @@ +/*- + * #%L + * athena-cloudwatch-metrics + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.cloudwatch.metrics; + +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.amazonaws.athena.connector.lambda.data.BlockUtils; +import com.amazonaws.athena.connector.lambda.data.S3BlockSpillReader; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import com.amazonaws.athena.connector.lambda.domain.spill.S3SpillLocation; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsResponse; +import com.amazonaws.athena.connector.lambda.records.RecordResponse; +import com.amazonaws.athena.connector.lambda.security.EncryptionKeyFactory; +import com.amazonaws.athena.connector.lambda.security.FederatedIdentity; +import com.amazonaws.athena.connector.lambda.security.LocalKeyFactory; +import com.amazonaws.athena.connectors.cloudwatch.metrics.tables.MetricSamplesTable; +import com.amazonaws.athena.connectors.cloudwatch.metrics.tables.MetricsTable; +import com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.cloudwatch.AmazonCloudWatch; +import com.amazonaws.services.cloudwatch.model.Dimension; +import com.amazonaws.services.cloudwatch.model.GetMetricDataRequest; +import com.amazonaws.services.cloudwatch.model.GetMetricDataResult; +import com.amazonaws.services.cloudwatch.model.ListMetricsRequest; +import com.amazonaws.services.cloudwatch.model.ListMetricsResult; +import com.amazonaws.services.cloudwatch.model.Metric; +import com.amazonaws.services.cloudwatch.model.MetricDataQuery; +import com.amazonaws.services.cloudwatch.model.MetricDataResult; +import com.amazonaws.services.cloudwatch.model.MetricStat; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.PutObjectResult; +import com.amazonaws.services.s3.model.S3Object; +import com.amazonaws.services.s3.model.S3ObjectInputStream; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.google.common.io.ByteStreams; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.runners.MockitoJUnitRunner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicLong; + +import static com.amazonaws.athena.connectors.cloudwatch.metrics.TestUtils.makeStringEquals; +import static com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table.DIMENSION_NAME_FIELD; +import static com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table.DIMENSION_VALUE_FIELD; +import static com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table.METRIC_NAME_FIELD; +import static com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table.NAMESPACE_FIELD; +import static com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table.PERIOD_FIELD; +import static com.amazonaws.athena.connectors.cloudwatch.metrics.tables.Table.STATISTIC_FIELD; +import static org.junit.Assert.*; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class MetricsRecordHandlerTest +{ + private static final Logger logger = LoggerFactory.getLogger(MetricsRecordHandlerTest.class); + //Schema for the metrics table. + private static final Table METRIC_TABLE = new MetricsTable(); + //Schema for the metric_samples table. + private static final Table METRIC_DATA_TABLE = new MetricSamplesTable(); + private static final TableName METRICS_TABLE_NAME = new TableName("default", METRIC_TABLE.getName()); + private static final TableName METRIC_SAMPLES_TABLE_NAME = new TableName("default", METRIC_DATA_TABLE.getName()); + + private FederatedIdentity identity = new FederatedIdentity("id", "principal", "account"); + private List mockS3Storage; + private MetricsRecordHandler handler; + private S3BlockSpillReader spillReader; + private BlockAllocator allocator; + private EncryptionKeyFactory keyFactory = new LocalKeyFactory(); + + @Mock + private AmazonCloudWatch mockMetrics; + + @Mock + private AmazonS3 mockS3; + + @Mock + private AWSSecretsManager mockSecretsManager; + + @Mock + private AmazonAthena mockAthena; + + @Before + public void setUp() + throws Exception + { + mockS3Storage = new ArrayList<>(); + allocator = new BlockAllocatorImpl(); + handler = new MetricsRecordHandler(mockS3, mockSecretsManager, mockAthena, mockMetrics); + spillReader = new S3BlockSpillReader(mockS3, allocator); + + when(mockS3.putObject(anyObject(), anyObject(), anyObject(), anyObject())) + .thenAnswer((InvocationOnMock invocationOnMock) -> + { + InputStream inputStream = (InputStream) invocationOnMock.getArguments()[2]; + ByteHolder byteHolder = new ByteHolder(); + byteHolder.setBytes(ByteStreams.toByteArray(inputStream)); + mockS3Storage.add(byteHolder); + return mock(PutObjectResult.class); + }); + + when(mockS3.getObject(anyString(), anyString())) + .thenAnswer((InvocationOnMock invocationOnMock) -> + { + S3Object mockObject = mock(S3Object.class); + ByteHolder byteHolder = mockS3Storage.get(0); + mockS3Storage.remove(0); + when(mockObject.getObjectContent()).thenReturn( + new S3ObjectInputStream( + new ByteArrayInputStream(byteHolder.getBytes()), null)); + return mockObject; + }); + } + + @After + public void tearDown() + throws Exception + { + allocator.close(); + } + + @Test + public void readMetricsWithConstraint() + throws Exception + { + logger.info("readMetricsWithConstraint: enter"); + + String namespace = "namespace"; + String dimName = "dimName"; + String dimValue = "dimValye"; + + int numMetrics = 100; + AtomicLong numCalls = new AtomicLong(0); + when(mockMetrics.listMetrics(any(ListMetricsRequest.class))).thenAnswer((InvocationOnMock invocation) -> { + ListMetricsRequest request = invocation.getArgumentAt(0, ListMetricsRequest.class); + numCalls.incrementAndGet(); + //assert that the namespace filter was indeed pushed down + assertEquals(namespace, request.getNamespace()); + String nextToken = (request.getNextToken() == null) ? "valid" : null; + List metrics = new ArrayList<>(); + + for (int i = 0; i < numMetrics; i++) { + metrics.add(new Metric().withNamespace(namespace).withMetricName("metric-" + i) + .withDimensions(new Dimension().withName(dimName).withValue(dimValue))); + metrics.add(new Metric().withNamespace(namespace + i).withMetricName("metric-" + i)); + } + + return new ListMetricsResult().withNextToken(nextToken).withMetrics(metrics); + }); + + Map constraintsMap = new HashMap<>(); + constraintsMap.put(NAMESPACE_FIELD, makeStringEquals(allocator, namespace)); + constraintsMap.put(DIMENSION_NAME_FIELD, makeStringEquals(allocator, dimName)); + constraintsMap.put(DIMENSION_VALUE_FIELD, makeStringEquals(allocator, dimValue)); + + S3SpillLocation spillLocation = S3SpillLocation.newBuilder() + .withBucket(UUID.randomUUID().toString()) + .withSplitId(UUID.randomUUID().toString()) + .withQueryId(UUID.randomUUID().toString()) + .withIsDirectory(true) + .build(); + + Split split = Split.newBuilder(spillLocation, keyFactory.create()).build(); + + ReadRecordsRequest request = new ReadRecordsRequest(identity, + "catalog", + "queryId-" + System.currentTimeMillis(), + METRICS_TABLE_NAME, + METRIC_TABLE.getSchema(), + split, + new Constraints(constraintsMap), + 100_000_000_000L, + 100_000_000_000L//100GB don't expect this to spill + ); + + RecordResponse rawResponse = handler.doReadRecords(allocator, request); + + assertTrue(rawResponse instanceof ReadRecordsResponse); + + ReadRecordsResponse response = (ReadRecordsResponse) rawResponse; + logger.info("readMetricsWithConstraint: rows[{}]", response.getRecordCount()); + + assertEquals(numCalls.get() * numMetrics, response.getRecords().getRowCount()); + logger.info("readMetricsWithConstraint: {}", BlockUtils.rowToString(response.getRecords(), 0)); + + logger.info("readMetricsWithConstraint: exit"); + } + + @Test + public void readMetricSamplesWithConstraint() + throws Exception + { + logger.info("readMetricSamplesWithConstraint: enter"); + + String namespace = "namespace"; + String metricName = "metricName"; + String statistic = "p90"; + String period = "60"; + String dimName = "dimName"; + String dimValue = "dimValue"; + List dimensions = Collections.singletonList(new Dimension().withName(dimName).withValue(dimValue)); + + int numMetrics = 10; + int numSamples = 10; + AtomicLong numCalls = new AtomicLong(0); + when(mockMetrics.getMetricData(any(GetMetricDataRequest.class))).thenAnswer((InvocationOnMock invocation) -> { + numCalls.incrementAndGet(); + return mockMetricData(invocation, numMetrics, numSamples); + }); + + Map constraintsMap = new HashMap<>(); + constraintsMap.put(NAMESPACE_FIELD, makeStringEquals(allocator, namespace)); + constraintsMap.put(STATISTIC_FIELD, makeStringEquals(allocator, statistic)); + constraintsMap.put(DIMENSION_NAME_FIELD, makeStringEquals(allocator, dimName)); + constraintsMap.put(DIMENSION_VALUE_FIELD, makeStringEquals(allocator, dimValue)); + + S3SpillLocation spillLocation = S3SpillLocation.newBuilder() + .withBucket(UUID.randomUUID().toString()) + .withSplitId(UUID.randomUUID().toString()) + .withQueryId(UUID.randomUUID().toString()) + .withIsDirectory(true) + .build(); + + Split split = Split.newBuilder(spillLocation, keyFactory.create()) + .add(DimensionSerDe.SERIALZIE_DIM_FIELD_NAME, DimensionSerDe.serialize(dimensions)) + .add(METRIC_NAME_FIELD, metricName) + .add(NAMESPACE_FIELD, namespace) + .add(STATISTIC_FIELD, statistic) + .add(PERIOD_FIELD, period) + .build(); + + ReadRecordsRequest request = new ReadRecordsRequest(identity, + "catalog", + "queryId-" + System.currentTimeMillis(), + METRIC_SAMPLES_TABLE_NAME, + METRIC_DATA_TABLE.getSchema(), + split, + new Constraints(constraintsMap), + 100_000_000_000L, + 100_000_000_000L//100GB don't expect this to spill + ); + + RecordResponse rawResponse = handler.doReadRecords(allocator, request); + + assertTrue(rawResponse instanceof ReadRecordsResponse); + + ReadRecordsResponse response = (ReadRecordsResponse) rawResponse; + logger.info("readMetricSamplesWithConstraint: rows[{}]", response.getRecordCount()); + + assertEquals(numCalls.get() * numMetrics * numSamples, response.getRecords().getRowCount()); + logger.info("readMetricSamplesWithConstraint: {}", BlockUtils.rowToString(response.getRecords(), 0)); + + logger.info("readMetricSamplesWithConstraint: exit"); + } + + private GetMetricDataResult mockMetricData(InvocationOnMock invocation, int numMetrics, int numSamples) + { + GetMetricDataRequest request = invocation.getArgumentAt(0, GetMetricDataRequest.class); + + /** + * Confirm that all available criteria were pushed down into Cloudwatch Metrics + */ + List queries = request.getMetricDataQueries(); + assertEquals(1, queries.size()); + MetricStat stat = queries.get(0).getMetricStat(); + assertNotNull(stat.getPeriod()); + assertNotNull(stat.getMetric()); + assertNotNull(stat.getStat()); + assertNotNull(stat.getMetric().getMetricName()); + assertNotNull(stat.getMetric().getNamespace()); + assertNotNull(stat.getMetric().getDimensions()); + assertEquals(1, stat.getMetric().getDimensions().size()); + + String nextToken = (request.getNextToken() == null) ? "valid" : null; + List samples = new ArrayList<>(); + + for (int i = 0; i < numMetrics; i++) { + List values = new ArrayList<>(); + List timestamps = new ArrayList<>(); + for (double j = 0; j < numSamples; j++) { + values.add(j); + timestamps.add(new Date(System.currentTimeMillis() + (int) j)); + } + samples.add(new MetricDataResult().withValues(values).withTimestamps(timestamps)); + } + + return new GetMetricDataResult().withNextToken(nextToken).withMetricDataResults(samples); + } + + private class ByteHolder + { + private byte[] bytes; + + public void setBytes(byte[] bytes) + { + this.bytes = bytes; + } + + public byte[] getBytes() + { + return bytes; + } + } +} diff --git a/athena-cloudwatch-metrics/src/test/java/com/amazonaws/athena/connectors/cloudwatch/metrics/TestUtils.java b/athena-cloudwatch-metrics/src/test/java/com/amazonaws/athena/connectors/cloudwatch/metrics/TestUtils.java new file mode 100644 index 0000000000..58fa82f59e --- /dev/null +++ b/athena-cloudwatch-metrics/src/test/java/com/amazonaws/athena/connectors/cloudwatch/metrics/TestUtils.java @@ -0,0 +1,36 @@ +/*- + * #%L + * athena-cloudwatch-metrics + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.cloudwatch.metrics; + +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.domain.predicate.EquatableValueSet; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import org.apache.arrow.vector.types.Types; + +public class TestUtils +{ + private TestUtils() {} + + public static ValueSet makeStringEquals(BlockAllocator allocator, String value) + { + return EquatableValueSet.newBuilder(allocator, Types.MinorType.VARCHAR.getType(), true, false) + .add(value).build(); + } +} diff --git a/athena-cloudwatch/LICENSE.txt b/athena-cloudwatch/LICENSE.txt new file mode 100644 index 0000000000..418de4c108 --- /dev/null +++ b/athena-cloudwatch/LICENSE.txt @@ -0,0 +1,174 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. \ No newline at end of file diff --git a/athena-cloudwatch/README.md b/athena-cloudwatch/README.md new file mode 100644 index 0000000000..03edd2e8fc --- /dev/null +++ b/athena-cloudwatch/README.md @@ -0,0 +1,60 @@ +# Amazon Athena Cloudwatch Connector + +This connector enables Amazon Athena to communicate with Cloudwatch, making your log data accessible via SQL. + +## Usage + +### Parameters + +The Athena Cloudwatch Connector exposes several configuration options via Lambda environment variables. More detail on the available parameters can be found below. + +1. **spill_bucket** - When the data returned by your Lambda function exceeds Lambda’s limits, this is the bucket that the data will be written to for Athena to read the excess from. (e.g. my_bucket) +2. **spill_prefix** - (Optional) Defaults to sub-folder in your bucket called 'athena-federation-spill'. Used in conjunction with spill_bucket, this is the path within the above bucket that large responses are spilled to. You should configure an S3 lifecycle on this location to delete old spills after X days/Hours. +3. **kms_key_id** - (Optional) By default any data that is spilled to S3 is encrypted using AES-GCM and a randomly generated key. Setting a KMS Key ID allows your Lambda function to use KMS for key generation for a stronger source of encryption keys. (e.g. a7e63k4b-8loc-40db-a2a1-4d0en2cd8331) +4. **disable_spill_encryption** - (Optional) Defaults to False so that any data that is spilled to S3 is encrypted using AES-GMC either with a randomly generated key or using KMS to generate keys. Setting this to false will disable spill encryption. You may wish to disable this for improved performance, especially if your spill location in S3 uses S3 Server Side Encryption. (e.g. True or False) + +The connector also supports AIMD Congestion Control for handling throttling events from Cloudwatch via the Athena Query Federation SDK's ThrottlingInvoker construct. You can tweak the default throttling behavior by setting any of the below (optional) environment variables: + +1. **throttle_initial_delay_ms** - (Default: 10ms) This is the initial call delay applied after the first congestion event. +1. **throttle_max_delay_ms** - (Default: 1000ms) This is the max delay between calls. You can derive TPS by dividing it into 1000ms. +1. **throttle_decrease_factor** - (Default: 0.5) This is the factor by which we reduce our call rate. +1. **throttle_increase_ms** - (Default: 10ms) This is the rate at which we decrease the call delay. + + +### Databases & Tables + +The Athena Cloudwatch Connector maps your LogGroups as schemas (aka database) and each LogStream as a table. The connector also maps a special "all_log_streams" View comprised of all LogStreams in the LogGroup. This View allows you to query all the logs in a LogGroup at once instead of search through each LogStream individually. + +Every Table mapped by the Athena Cloudwatch Connector has the following schema which matches the fields provided by Cloudwatch Logs itself. + +1. **log_stream** - A VARCHAR containing the name of the LogStream that the row is from. +2. **time** - An INT64 containing the epoch time of the log line was generated. +3. **message** - A VARCHAR containing the log message itself. + +### Required Permissions + +Review the "Policies" section of the athena-cloudwatch.yaml file for full details on the IAM Policies required by this connector. A brief summary is below. + +1. S3 Write Access - In order to successfully handle large queries, the connector requires write access to a location in S3. +2. CloudWatch Logs Read/Write - The connector uses this access to read your log data in order to satisfy your queries but also to write its own diagnostic logs. +1. Athena GetQueryExecution - The connector uses this access to fast-fail when the upstream Athena query has terminated. + +### Deploying The Connector + +To use this connector in your queries, navigate to AWS Serverless Application Repository and deploy a pre-built version of this connector. Alternatively, you can build and deploy this connector from source follow the below steps or use the more detailed tutorial in the athena-example module: + +1. From the athena-federation-sdk dir, run `mvn clean install` if you haven't already. +2. From the athena-cloudwatch dir, run `mvn clean install`. +3. From the athena-cloudwatch dir, run `../tools/publish.sh S3_BUCKET_NAME athena-cloudwatch` to publish the connector to your private AWS Serverless Application Repository. The S3_BUCKET in the command is where a copy of the connector's code will be stored for Serverless Application Repository to retrieve it. This will allow users with permission to do so, the ability to deploy instances of the connector via 1-Click form. Then navigate to [Serverless Application Repository](https://aws.amazon.com/serverless/serverlessrepo) +4. Try running a query like the one below in Athena: +```sql +select * from "lambda:"."/aws/lambda/".all_log_streams limit 100 +``` + +## Performance + +The Athena Cloudwatch Connector will attempt to parallelize queries against Cloudwatch by parallelizing scans of the various log_streams needed for your query. Predicate Pushdown is performed within the Lambda function and also within Cloudwatch Logs for certain time period filters. + +## License + +This project is licensed under the Apache-2.0 License. \ No newline at end of file diff --git a/athena-cloudwatch/athena-cloudwatch.yaml b/athena-cloudwatch/athena-cloudwatch.yaml new file mode 100644 index 0000000000..ce4915aae4 --- /dev/null +++ b/athena-cloudwatch/athena-cloudwatch.yaml @@ -0,0 +1,71 @@ +Transform: 'AWS::Serverless-2016-10-31' +Metadata: + 'AWS::ServerlessRepo::Application': + Name: AthenaCloudwatchConnector + Description: 'This connector enables Amazon Athena to communicate with Cloudwatch, making your logs accessible via SQL.' + Author: 'Amazon Athena' + SpdxLicenseId: Apache-2.0 + LicenseUrl: LICENSE.txt + ReadmeUrl: README.md + Labels: + - athena-federation + HomePageUrl: 'https://github.com/awslabs/aws-athena-query-federation' + SemanticVersion: 1.0.0 + SourceCodeUrl: 'https://github.com/awslabs/aws-athena-query-federation' +Parameters: + AthenaCatalogName: + Description: 'The name you will give to this catalog in Athena. It will also be used as the function name.' + Type: String + SpillBucket: + Description: 'The bucket where this function can spill data.' + Type: String + SpillPrefix: + Description: 'The bucket prefix where this function can spill large responses.' + Type: String + Default: athena-spill + LambdaTimeout: + Description: 'Maximum Lambda invocation runtime in seconds. (min 1 - 900 max)' + Default: 900 + Type: Number + LambdaMemory: + Description: 'Lambda memory in MB (min 128 - 3008 max).' + Default: 3008 + Type: Number + DisableSpillEncryption: + Description: "WARNING: If set to 'true' encryption for spilled data is disabled." + Default: 'false' + Type: String +Resources: + ConnectorConfig: + Type: 'AWS::Serverless::Function' + Properties: + Environment: + Variables: + disable_spill_encryption: !Ref DisableSpillEncryption + spill_bucket: !Ref SpillBucket + spill_prefix: !Ref SpillPrefix + FunctionName: !Ref AthenaCatalogName + Handler: "com.amazonaws.athena.connectors.cloudwatch.CloudwatchCompositeHandler" + CodeUri: "./target/athena-cloudwatch-1.0.jar" + Description: "Enables Amazon Athena to communicate with Cloudwatch, making your log accessible via SQL" + Runtime: java8 + Timeout: !Ref LambdaTimeout + MemorySize: !Ref LambdaMemory + Policies: + - Statement: + - Action: + - logs:Describe* + - logs:Get* + - logs:List* + - logs:StartQuery + - logs:StopQuery + - logs:TestMetricFilter + - logs:FilterLogEvents + - athena:GetQueryExecution + Effect: Allow + Resource: '*' + Version: '2012-10-17' + #S3CrudPolicy allows our connector to spill large responses to S3. You can optionally replace this pre-made policy + #with one that is more restrictive and can only 'put' but not read,delete, or overwrite files. + - S3CrudPolicy: + BucketName: !Ref SpillBucket \ No newline at end of file diff --git a/athena-cloudwatch/pom.xml b/athena-cloudwatch/pom.xml new file mode 100644 index 0000000000..02fa9a54d6 --- /dev/null +++ b/athena-cloudwatch/pom.xml @@ -0,0 +1,57 @@ + + + + aws-athena-query-federation + com.amazonaws + 1.0 + + 4.0.0 + + athena-cloudwatch + + + + com.amazonaws + aws-athena-federation-sdk + ${aws-athena-federation-sdk.version} + + + com.amazonaws + aws-java-sdk-logs + 1.11.490 + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.1 + + false + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + package + + shade + + + + + + + \ No newline at end of file diff --git a/athena-cloudwatch/src/main/java/com/amazonaws/athena/connectors/cloudwatch/CloudwatchCompositeHandler.java b/athena-cloudwatch/src/main/java/com/amazonaws/athena/connectors/cloudwatch/CloudwatchCompositeHandler.java new file mode 100644 index 0000000000..0db8b25753 --- /dev/null +++ b/athena-cloudwatch/src/main/java/com/amazonaws/athena/connectors/cloudwatch/CloudwatchCompositeHandler.java @@ -0,0 +1,35 @@ +/*- + * #%L + * athena-cloudwatch + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.cloudwatch; + +import com.amazonaws.athena.connector.lambda.handlers.CompositeHandler; + +/** + * Boilerplate composite handler that allows us to use a single Lambda function for both + * Metadata and Data. In this case we just compose CloudwatchMetadataHandler and CloudwatchRecordHandler. + */ +public class CloudwatchCompositeHandler + extends CompositeHandler +{ + public CloudwatchCompositeHandler() + { + super(new CloudwatchMetadataHandler(), new CloudwatchRecordHandler()); + } +} diff --git a/athena-cloudwatch/src/main/java/com/amazonaws/athena/connectors/cloudwatch/CloudwatchExceptionFilter.java b/athena-cloudwatch/src/main/java/com/amazonaws/athena/connectors/cloudwatch/CloudwatchExceptionFilter.java new file mode 100644 index 0000000000..c71db552cf --- /dev/null +++ b/athena-cloudwatch/src/main/java/com/amazonaws/athena/connectors/cloudwatch/CloudwatchExceptionFilter.java @@ -0,0 +1,45 @@ +/*- + * #%L + * athena-cloudwatch + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.cloudwatch; + +import com.amazonaws.athena.connector.lambda.ThrottlingInvoker; +import com.amazonaws.services.logs.model.AWSLogsException; +import com.amazonaws.services.logs.model.LimitExceededException; + +/** + * Used to identify Exceptions that are related to Cloudwatch Logs throttling events. + */ +public class CloudwatchExceptionFilter + implements ThrottlingInvoker.ExceptionFilter +{ + public static final ThrottlingInvoker.ExceptionFilter EXCEPTION_FILTER = new CloudwatchExceptionFilter(); + + private CloudwatchExceptionFilter() {} + + @Override + public boolean isMatch(Exception ex) + { + if (ex instanceof AWSLogsException && ex.getMessage().startsWith("Rate exceeded")) { + return true; + } + + return (ex instanceof LimitExceededException); + } +} diff --git a/athena-cloudwatch/src/main/java/com/amazonaws/athena/connectors/cloudwatch/CloudwatchMetadataHandler.java b/athena-cloudwatch/src/main/java/com/amazonaws/athena/connectors/cloudwatch/CloudwatchMetadataHandler.java new file mode 100644 index 0000000000..d88b39d5a2 --- /dev/null +++ b/athena-cloudwatch/src/main/java/com/amazonaws/athena/connectors/cloudwatch/CloudwatchMetadataHandler.java @@ -0,0 +1,345 @@ +/*- + * #%L + * athena-cloudwatch + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.cloudwatch; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.ThrottlingInvoker; +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockWriter; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.spill.SpillLocation; +import com.amazonaws.athena.connector.lambda.handlers.MetadataHandler; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesResponse; +import com.amazonaws.athena.connector.lambda.security.EncryptionKeyFactory; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.logs.AWSLogs; +import com.amazonaws.services.logs.AWSLogsClientBuilder; +import com.amazonaws.services.logs.model.DescribeLogGroupsRequest; +import com.amazonaws.services.logs.model.DescribeLogGroupsResult; +import com.amazonaws.services.logs.model.DescribeLogStreamsRequest; +import com.amazonaws.services.logs.model.DescribeLogStreamsResult; +import com.amazonaws.services.logs.model.LogStream; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import org.apache.arrow.util.VisibleForTesting; +import org.apache.arrow.vector.complex.reader.FieldReader; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.arrow.vector.types.pojo.Schema; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.TimeoutException; + +import static com.amazonaws.athena.connectors.cloudwatch.CloudwatchExceptionFilter.EXCEPTION_FILTER; + +/** + * Handles metadata requests for the Athena Cloudwatch Connector. + *

+ * For more detail, please see the module's README.md, some notable characteristics of this class include: + *

+ * 1. Each LogGroup is treated as a schema (aka database). + * 2. Each LogStream is treated as a table. + * 3. A special 'all_log_streams' view is added which allows you to query all LogStreams in a LogGroup. + * 4. LogStreams area treated as partitions and scanned in parallel. + * 5. Timestamp predicates are pushed into Cloudwatch itself. + */ +public class CloudwatchMetadataHandler + extends MetadataHandler +{ + private static final Logger logger = LoggerFactory.getLogger(CloudwatchMetadataHandler.class); + + //Used to tag log lines generated by this connector for diagnostic purposes when interacting with Athena. + private static final String sourceType = "cloudwatch"; + //some customers have a very large number of log groups and log streams. In those cases we limit + //the max results as a safety mechanism. They can still be queried but aren't returned in show tables or show databases. + private static final long MAX_RESULTS = 100_000; + //The maximum number of splits that will be generated by a single call to doGetSplits(...) before we paginate. + protected static final int MAX_SPLITS_PER_REQUEST = 1000; + //The name of the special table view which allows you to query all log streams in a LogGroup + protected static final String ALL_LOG_STREAMS_TABLE = "all_log_streams"; + //The name of the log stream field in our response and split objects. + protected static final String LOG_STREAM_FIELD = "log_stream"; + //The name of the log group field in our response and split objects. + protected static final String LOG_GROUP_FIELD = "log_group"; + //The name of the log time field in our response and split objects. + protected static final String LOG_TIME_FIELD = "time"; + //The name of the log message field in our response and split objects. + protected static final String LOG_MSG_FIELD = "message"; + //The name of the log stream size field in our split objects. + protected static final String LOG_STREAM_SIZE_FIELD = "log_stream_bytes"; + //The the schema of all Cloudwatch tables. + protected static final Schema CLOUDWATCH_SCHEMA; + + static { + CLOUDWATCH_SCHEMA = new SchemaBuilder().newBuilder() + .addField(LOG_STREAM_FIELD, Types.MinorType.VARCHAR.getType()) + .addField(LOG_TIME_FIELD, new ArrowType.Int(64, true)) + .addField(LOG_MSG_FIELD, Types.MinorType.VARCHAR.getType()) + //requests to read multiple log streams can be parallelized so lets treat it like a partition + .addMetadata("partitionCols", LOG_STREAM_FIELD) + .build(); + } + + private final AWSLogs awsLogs; + private final ThrottlingInvoker invoker = ThrottlingInvoker.newDefaultBuilder(EXCEPTION_FILTER).build(); + private final CloudwatchTableResolver tableResolver; + + public CloudwatchMetadataHandler() + { + super(sourceType); + this.awsLogs = AWSLogsClientBuilder.standard().build(); + tableResolver = new CloudwatchTableResolver(invoker, awsLogs, MAX_RESULTS, MAX_RESULTS); + } + + @VisibleForTesting + protected CloudwatchMetadataHandler(AWSLogs awsLogs, + EncryptionKeyFactory keyFactory, + AWSSecretsManager secretsManager, + AmazonAthena athena, + String spillBucket, + String spillPrefix) + { + super(keyFactory, secretsManager, athena, sourceType, spillBucket, spillPrefix); + this.awsLogs = awsLogs; + tableResolver = new CloudwatchTableResolver(invoker, awsLogs, MAX_RESULTS, MAX_RESULTS); + } + + /** + * List LogGroups in your Cloudwatch account treating each as a 'schema' (aka database) + * + * @see MetadataHandler + */ + @Override + public ListSchemasResponse doListSchemaNames(BlockAllocator blockAllocator, ListSchemasRequest listSchemasRequest) + throws TimeoutException + { + DescribeLogGroupsRequest request = new DescribeLogGroupsRequest(); + DescribeLogGroupsResult result; + List schemas = new ArrayList<>(); + do { + if (schemas.size() > MAX_RESULTS) { + throw new RuntimeException("Too many log groups, exceeded max metadata results for schema count."); + } + result = invoker.invoke(() -> awsLogs.describeLogGroups(request)); + result.getLogGroups().forEach(next -> schemas.add(next.getLogGroupName().toLowerCase())); + request.setNextToken(result.getNextToken()); + logger.info("doListSchemaNames: Listing log groups {} {}", result.getNextToken(), schemas.size()); + } + while (result.getNextToken() != null); + + return new ListSchemasResponse(listSchemasRequest.getCatalogName(), schemas); + } + + /** + * List LogStreams within the requested schema (aka LogGroup) in your Cloudwatch account treating each as a 'table'. + * + * @see MetadataHandler + */ + @Override + public ListTablesResponse doListTables(BlockAllocator blockAllocator, ListTablesRequest listTablesRequest) + throws TimeoutException + { + String logGroupName = tableResolver.validateSchema(listTablesRequest.getSchemaName()); + DescribeLogStreamsRequest request = new DescribeLogStreamsRequest(logGroupName); + DescribeLogStreamsResult result; + List tables = new ArrayList<>(); + do { + if (tables.size() > MAX_RESULTS) { + throw new RuntimeException("Too many log streams, exceeded max metadata results for table count."); + } + result = invoker.invoke(() -> awsLogs.describeLogStreams(request)); + result.getLogStreams().forEach(next -> tables.add(toTableName(listTablesRequest, next))); + request.setNextToken(result.getNextToken()); + logger.info("doListTables: Listing log streams {} {}", result.getNextToken(), tables.size()); + } + while (result.getNextToken() != null); + + //We add a special table that represents all log streams. This is helpful depending on how + //you have your logs organized. + tables.add(new TableName(listTablesRequest.getSchemaName(), ALL_LOG_STREAMS_TABLE)); + + return new ListTablesResponse(listTablesRequest.getCatalogName(), tables); + } + + /** + * Returns the pre-set schema for the request Cloudwatch table (LogStream) and schema (LogGroup) after + * validating that it exists. + * + * @see MetadataHandler + */ + @Override + public GetTableResponse doGetTable(BlockAllocator blockAllocator, GetTableRequest getTableRequest) + { + TableName tableName = getTableRequest.getTableName(); + tableResolver.validateTable(tableName); + return new GetTableResponse(getTableRequest.getCatalogName(), + getTableRequest.getTableName(), + CLOUDWATCH_SCHEMA, + Collections.singleton(LOG_STREAM_FIELD)); + } + + /** + * We add one additional field to the partition schema. This field is used for our own purposes and ignored + * by Athena but it will get passed to calls to GetSplits(...) which is where we will set it on our Split + * without the need to call Cloudwatch a second time. + * + * @see MetadataHandler + */ + @Override + public void enhancePartitionSchema(SchemaBuilder partitionSchemaBuilder, GetTableLayoutRequest request) + { + partitionSchemaBuilder.addField(LOG_STREAM_SIZE_FIELD, new ArrowType.Int(64, true)); + partitionSchemaBuilder.addField(LOG_GROUP_FIELD, Types.MinorType.VARCHAR.getType()); + } + + /** + * Gets the list of LogStreams that need to be scanned to satisfy the requested table. In most cases this will be just + * 1 LogStream and this results in just 1 partition. If, however, the request is for the special ALL_LOG_STREAMS view + * then all LogStreams in the requested LogGroup (schema) are queried and turned into partitions 1:1. + * + * @note This method applies partition pruning based on the log_stream field. + * @see MetadataHandler + */ + @Override + public void getPartitions(BlockWriter blockWriter, GetTableLayoutRequest request, QueryStatusChecker queryStatusChecker) + throws Exception + { + CloudwatchTableName cwTableName = tableResolver.validateTable(request.getTableName()); + + DescribeLogStreamsRequest cwRequest = new DescribeLogStreamsRequest(cwTableName.getLogGroupName()); + if (!ALL_LOG_STREAMS_TABLE.equals(cwTableName.getLogStreamName())) { + cwRequest.setLogStreamNamePrefix(cwTableName.getLogStreamName()); + } + + DescribeLogStreamsResult result; + do { + result = invoker.invoke(() -> awsLogs.describeLogStreams(cwRequest)); + for (LogStream next : result.getLogStreams()) { + //Each log stream that matches any possible partition pruning should be added to the partition list. + blockWriter.writeRows((Block block, int rowNum) -> { + boolean matched = block.setValue(LOG_GROUP_FIELD, rowNum, cwRequest.getLogGroupName()); + matched &= block.setValue(LOG_STREAM_FIELD, rowNum, next.getLogStreamName()); + matched &= block.setValue(LOG_STREAM_SIZE_FIELD, rowNum, next.getStoredBytes()); + return matched ? 1 : 0; + }); + } + cwRequest.setNextToken(result.getNextToken()); + } + while (result.getNextToken() != null && queryStatusChecker.isQueryRunning()); + } + + /** + * Each partition is converted into a single Split which means we will potentially read all LogStreams required for + * the query in parallel. + * + * @see MetadataHandler + */ + @Override + public GetSplitsResponse doGetSplits(BlockAllocator allocator, GetSplitsRequest request) + { + int partitionContd = decodeContinuationToken(request); + Set splits = new HashSet<>(); + Block partitions = request.getPartitions(); + for (int curPartition = partitionContd; curPartition < partitions.getRowCount(); curPartition++) { + FieldReader logStreamReader = partitions.getFieldReader(LOG_STREAM_FIELD); + logStreamReader.setPosition(curPartition); + + FieldReader logGroupReader = partitions.getFieldReader(LOG_GROUP_FIELD); + logGroupReader.setPosition(curPartition); + + FieldReader sizeReader = partitions.getFieldReader(LOG_STREAM_SIZE_FIELD); + sizeReader.setPosition(curPartition); + + //Every split must have a unique location if we wish to spill to avoid failures + SpillLocation spillLocation = makeSpillLocation(request); + + Split.Builder splitBuilder = Split.newBuilder(spillLocation, makeEncryptionKey()) + .add(CloudwatchMetadataHandler.LOG_GROUP_FIELD, String.valueOf(logGroupReader.readText())) + .add(CloudwatchMetadataHandler.LOG_STREAM_FIELD, String.valueOf(logStreamReader.readText())) + .add(CloudwatchMetadataHandler.LOG_STREAM_SIZE_FIELD, String.valueOf(sizeReader.readLong())); + + splits.add(splitBuilder.build()); + + if (splits.size() >= MAX_SPLITS_PER_REQUEST) { + //We exceeded the number of split we want to return in a single request, return and provide + //a continuation token. + return new GetSplitsResponse(request.getCatalogName(), + splits, + encodeContinuationToken(curPartition)); + } + } + + return new GetSplitsResponse(request.getCatalogName(), splits, null); + } + + /** + * Used to handle paginated requests. + * + * @return The partition number to resume with. + */ + private int decodeContinuationToken(GetSplitsRequest request) + { + if (request.hasContinuationToken()) { + return Integer.valueOf(request.getContinuationToken()); + } + + //No continuation token present + return 0; + } + + /** + * Used to create pagination tokens by encoding the number of the next partition to process. + * + * @param partition The number of the next partition we should process on the next call. + * @return The encoded continuation token. + */ + private String encodeContinuationToken(int partition) + { + return String.valueOf(partition); + } + + /** + * Helper that converts a LogStream to a TableName by lowercasing the schema of the request and the logstreamname. + * + * @param request The ListTablesRequest to retrieve the schema name from. + * @param logStream The LogStream to turn into a table. + * @return A TableName with both the schema (LogGroup) and the table (LogStream) lowercased. + */ + private TableName toTableName(ListTablesRequest request, LogStream logStream) + { + return new TableName(request.getSchemaName(), logStream.getLogStreamName().toLowerCase()); + } +} diff --git a/athena-cloudwatch/src/main/java/com/amazonaws/athena/connectors/cloudwatch/CloudwatchRecordHandler.java b/athena-cloudwatch/src/main/java/com/amazonaws/athena/connectors/cloudwatch/CloudwatchRecordHandler.java new file mode 100644 index 0000000000..78388c20bb --- /dev/null +++ b/athena-cloudwatch/src/main/java/com/amazonaws/athena/connectors/cloudwatch/CloudwatchRecordHandler.java @@ -0,0 +1,172 @@ +/*- + * #%L + * athena-cloudwatch + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.cloudwatch; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.ThrottlingInvoker; +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockSpiller; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.domain.predicate.Range; +import com.amazonaws.athena.connector.lambda.domain.predicate.SortedRangeSet; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import com.amazonaws.athena.connector.lambda.handlers.RecordHandler; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.athena.AmazonAthenaClientBuilder; +import com.amazonaws.services.logs.AWSLogs; +import com.amazonaws.services.logs.AWSLogsClientBuilder; +import com.amazonaws.services.logs.model.GetLogEventsRequest; +import com.amazonaws.services.logs.model.GetLogEventsResult; +import com.amazonaws.services.logs.model.OutputLogEvent; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.amazonaws.services.secretsmanager.AWSSecretsManagerClientBuilder; +import org.apache.arrow.util.VisibleForTesting; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicLong; + +import static com.amazonaws.athena.connectors.cloudwatch.CloudwatchExceptionFilter.EXCEPTION_FILTER; +import static com.amazonaws.athena.connectors.cloudwatch.CloudwatchMetadataHandler.LOG_GROUP_FIELD; +import static com.amazonaws.athena.connectors.cloudwatch.CloudwatchMetadataHandler.LOG_MSG_FIELD; +import static com.amazonaws.athena.connectors.cloudwatch.CloudwatchMetadataHandler.LOG_STREAM_FIELD; +import static com.amazonaws.athena.connectors.cloudwatch.CloudwatchMetadataHandler.LOG_TIME_FIELD; + +/** + * Handles data read record requests for the Athena Cloudwatch Connector. + *

+ * For more detail, please see the module's README.md, some notable characteristics of this class include: + *

+ * 1. Reads and maps Cloudwatch Logs data for a specific LogStream (split) + * 2. Attempts to push down time range predicates into Cloudwatch. + */ +public class CloudwatchRecordHandler + extends RecordHandler +{ + private static final Logger logger = LoggerFactory.getLogger(CloudwatchRecordHandler.class); + //Used to tag log lines generated by this connector for diagnostic purposes when interacting with Athena. + private static final String sourceType = "cloudwatch"; + //Used to handle Throttling events and apply AIMD congestion control + ThrottlingInvoker invoker = ThrottlingInvoker.newDefaultBuilder(EXCEPTION_FILTER).build(); + private final AtomicLong count = new AtomicLong(0); + private final AWSLogs awsLogs; + + public CloudwatchRecordHandler() + { + this(AmazonS3ClientBuilder.defaultClient(), + AWSSecretsManagerClientBuilder.defaultClient(), + AmazonAthenaClientBuilder.defaultClient(), + AWSLogsClientBuilder.defaultClient()); + } + + @VisibleForTesting + protected CloudwatchRecordHandler(AmazonS3 amazonS3, AWSSecretsManager secretsManager, AmazonAthena athena, AWSLogs awsLogs) + { + super(amazonS3, secretsManager, athena, sourceType); + this.awsLogs = awsLogs; + } + + /** + * Scans Cloudwatch Logs using the LogStream and optional Time stamp filters. + * + * @see RecordHandler + */ + @Override + protected void readWithConstraint(BlockSpiller spiller, ReadRecordsRequest recordsRequest, QueryStatusChecker queryStatusChecker) + throws TimeoutException + { + String continuationToken = null; + TableName tableName = recordsRequest.getTableName(); + Split split = recordsRequest.getSplit(); + invoker.setBlockSpiller(spiller); + do { + final String actualContinuationToken = continuationToken; + GetLogEventsResult logEventsResult = invoker.invoke(() -> awsLogs.getLogEvents( + pushDownConstraints(recordsRequest.getConstraints(), + new GetLogEventsRequest() + .withLogGroupName(split.getProperty(LOG_GROUP_FIELD)) + //We use the property instead of the table name because of the special all_streams table + .withLogStreamName(split.getProperty(LOG_STREAM_FIELD)) + .withNextToken(actualContinuationToken) + ))); + + if (continuationToken == null || !continuationToken.equals(logEventsResult.getNextForwardToken())) { + continuationToken = logEventsResult.getNextForwardToken(); + } + else { + continuationToken = null; + } + + for (OutputLogEvent ole : logEventsResult.getEvents()) { + spiller.writeRows((Block block, int rowNum) -> { + boolean matched = true; + matched &= block.offerValue(LOG_STREAM_FIELD, rowNum, split.getProperty(LOG_STREAM_FIELD)); + matched &= block.offerValue(LOG_TIME_FIELD, rowNum, ole.getTimestamp()); + matched &= block.offerValue(LOG_MSG_FIELD, rowNum, ole.getMessage()); + return matched ? 1 : 0; + }); + } + + logger.info("readWithConstraint: LogGroup[{}] LogStream[{}] Continuation[{}] rows[{}]", + tableName.getSchemaName(), tableName.getTableName(), continuationToken, + logEventsResult.getEvents().size()); + } + while (continuationToken != null && queryStatusChecker.isQueryRunning()); + } + + /** + * Attempts to push down predicates into Cloudwatch Logs by decorating the Cloudwatch Logs request. + * + * @param constraints The constraints for the read as provided by Athena based on the customer's query. + * @param request The Cloudwatch Logs request to inject predicates to. + * @return The decorated Cloudwatch Logs request. + * @note This impl currently only pushing down SortedRangeSet filters (>=, =<, between) on the log time column. + */ + private GetLogEventsRequest pushDownConstraints(Constraints constraints, GetLogEventsRequest request) + { + ValueSet timeConstraint = constraints.getSummary().get(LOG_TIME_FIELD); + if (timeConstraint instanceof SortedRangeSet && !timeConstraint.isNullAllowed()) { + //SortedRangeSet is how >, <, between is represented which are easiest and most common when + //searching logs so we attempt to push that down here as an optimization. SQL can represent complex + //overlapping ranges which Cloudwatch can not support so this is not a replacement for applying + //constraints using the ConstraintEvaluator. + + Range basicPredicate = ((SortedRangeSet) timeConstraint).getSpan(); + + if (!basicPredicate.getLow().isNullValue()) { + Long lowerBound = (Long) basicPredicate.getLow().getValue(); + request.setStartTime(lowerBound); + } + + if (!basicPredicate.getHigh().isNullValue()) { + Long upperBound = (Long) basicPredicate.getHigh().getValue(); + request.setEndTime(upperBound); + } + } + + return request; + } +} diff --git a/athena-cloudwatch/src/main/java/com/amazonaws/athena/connectors/cloudwatch/CloudwatchTableName.java b/athena-cloudwatch/src/main/java/com/amazonaws/athena/connectors/cloudwatch/CloudwatchTableName.java new file mode 100644 index 0000000000..7e083ebc71 --- /dev/null +++ b/athena-cloudwatch/src/main/java/com/amazonaws/athena/connectors/cloudwatch/CloudwatchTableName.java @@ -0,0 +1,80 @@ +/*- + * #%L + * athena-cloudwatch + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.cloudwatch; + +import com.amazonaws.athena.connector.lambda.domain.TableName; + +import java.util.Objects; + +public class CloudwatchTableName +{ + private final String logGroupName; + private final String logStreamName; + + public CloudwatchTableName(String logGroupName, String logStreamName) + { + this.logGroupName = logGroupName; + this.logStreamName = logStreamName; + } + + public String getLogGroupName() + { + return logGroupName; + } + + public String getLogStreamName() + { + return logStreamName; + } + + public TableName toTableName() + { + return new TableName(logGroupName.toLowerCase(), logStreamName.toLowerCase()); + } + + @Override + public String toString() + { + return "CloudwatchTableName{" + + "logGroupName='" + logGroupName + '\'' + + ", logStreamName='" + logStreamName + '\'' + + '}'; + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + CloudwatchTableName that = (CloudwatchTableName) o; + return Objects.equals(getLogGroupName(), that.getLogGroupName()) && + Objects.equals(getLogStreamName(), that.getLogStreamName()); + } + + @Override + public int hashCode() + { + return Objects.hash(getLogGroupName(), getLogStreamName()); + } +} diff --git a/athena-cloudwatch/src/main/java/com/amazonaws/athena/connectors/cloudwatch/CloudwatchTableResolver.java b/athena-cloudwatch/src/main/java/com/amazonaws/athena/connectors/cloudwatch/CloudwatchTableResolver.java new file mode 100644 index 0000000000..52526f5498 --- /dev/null +++ b/athena-cloudwatch/src/main/java/com/amazonaws/athena/connectors/cloudwatch/CloudwatchTableResolver.java @@ -0,0 +1,289 @@ +/*- + * #%L + * athena-cloudwatch + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.cloudwatch; + +import com.amazonaws.athena.connector.lambda.ThrottlingInvoker; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.services.logs.AWSLogs; +import com.amazonaws.services.logs.model.DescribeLogGroupsRequest; +import com.amazonaws.services.logs.model.DescribeLogGroupsResult; +import com.amazonaws.services.logs.model.DescribeLogStreamsRequest; +import com.amazonaws.services.logs.model.DescribeLogStreamsResult; +import com.amazonaws.services.logs.model.LogGroup; +import com.amazonaws.services.logs.model.LogStream; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +import static com.amazonaws.athena.connectors.cloudwatch.CloudwatchMetadataHandler.ALL_LOG_STREAMS_TABLE; + +/** + * This class helps with resolving the differences in casing between cloudwatch log and Presto. Presto expects all + * databases, tables, and columns to be lower case. This class allows us to use cloudwatch logGroups and logStreams + * which may have captial letters in them without issue. It does so by caching LogStreams and LogStreams and doing + * a case insentive search over them. It will first try to do a targeted get to reduce the penalty for LogGroups + * and LogStreams which don't have capitalization. It also has an optimization for LAMBDA which is a common + * cause of capitalized LogStreams by doing a targeted replace for LAMBDA's pattern. + */ +public class CloudwatchTableResolver +{ + private static final Logger logger = LoggerFactory.getLogger(CloudwatchTableResolver.class); + + private AWSLogs awsLogs; + //Used to handle Throttling events using an AIMD strategy for congestion control. + private ThrottlingInvoker invoker; + //The LogStream pattern that is capitalized by LAMBDA + private static final String LAMBDA_PATTERN = "$latest"; + //The LogStream pattern to replace + private static final String LAMBDA_ACTUAL_PATTERN = "$LATEST"; + //The schema cache that is presto casing to cloudwatch casing + private final LoadingCache schemaCache; + //The table cache that is presto casing to cloudwatch casing + private final LoadingCache tableCache; + + /** + * Constructs an instance of the table resolver. + * + * @param invoker The ThrottlingInvoker to use to handle throttling events. + * @param awsLogs The AWSLogs client to use for cache misses. + * @param maxSchemaCacheSize The max number of schemas to cache. + * @param maxTableCacheSize The max tables to cache. + */ + public CloudwatchTableResolver(ThrottlingInvoker invoker, AWSLogs awsLogs, long maxSchemaCacheSize, long maxTableCacheSize) + { + this.invoker = invoker; + this.awsLogs = awsLogs; + this.tableCache = CacheBuilder.newBuilder() + .maximumSize(maxTableCacheSize) + .build( + new CacheLoader() + { + public CloudwatchTableName load(TableName schemaName) + throws TimeoutException + { + return loadLogStreams(schemaName.getSchemaName(), schemaName.getTableName()); + } + }); + + this.schemaCache = CacheBuilder.newBuilder() + .maximumSize(maxSchemaCacheSize) + .build( + new CacheLoader() + { + public String load(String schemaName) + throws TimeoutException + { + return loadLogGroups(schemaName); + } + }); + } + + /** + * Loads the requested LogStream as identified by the TableName. + * + * @param logGroup The properly cased schema name. + * @param logStream The table name to validate. + * @return The CloudwatchTableName or null if not found. + * @note This method also primes the cache with other CloudwatchTableNames found along the way while scaning Cloudwatch. + */ + private CloudwatchTableName loadLogStreams(String logGroup, String logStream) + throws TimeoutException + { + //As an optimization, see if the table name is an exact match (meaning likely no casing issues) + CloudwatchTableName result = loadLogStream(logGroup, logStream); + if (result != null) { + return result; + } + + logger.info("loadLogStreams: Did not find a match for the table, falling back to LogGroup scan for {}:{}", + logGroup, logStream); + DescribeLogStreamsRequest validateTableRequest = new DescribeLogStreamsRequest(logGroup); + DescribeLogStreamsResult validateTableResult; + do { + validateTableResult = invoker.invoke(() -> awsLogs.describeLogStreams(validateTableRequest)); + for (LogStream nextStream : validateTableResult.getLogStreams()) { + String logStreamName = nextStream.getLogStreamName(); + CloudwatchTableName nextCloudwatch = new CloudwatchTableName(logGroup, logStreamName); + tableCache.put(nextCloudwatch.toTableName(), nextCloudwatch); + if (nextCloudwatch.getLogStreamName().equalsIgnoreCase(logStreamName)) { + //We stop loading once we find the one we care about. This is an optimization that + //attempt to exploit the fact that we likely access more recent logstreams first. + logger.info("loadLogStreams: Matched {} for {}", nextCloudwatch, logStream); + return nextCloudwatch; + } + } + validateTableRequest.setNextToken(validateTableResult.getNextToken()); + } + while (validateTableResult.getNextToken() != null); + + //We could not find a match + throw new IllegalArgumentException("No such table " + logGroup + " " + logStream); + } + + /** + * Optomizaiton that attempts to load a specific LogStream as identified by the TableName. + * + * @param logGroup The properly cased schema name. + * @param logStream The table name to validate. + * @return The CloudwatchTableName or null if not found. + * @note This method also primes the cache with other CloudwatchTableNames found along the way while scanning Cloudwatch. + */ + private CloudwatchTableName loadLogStream(String logGroup, String logStream) + throws TimeoutException + { + if (ALL_LOG_STREAMS_TABLE.equalsIgnoreCase(logStream)) { + return new CloudwatchTableName(logGroup, ALL_LOG_STREAMS_TABLE); + } + + String effectiveTableName = logStream; + if (effectiveTableName.contains(LAMBDA_PATTERN)) { + logger.info("loadLogStream: Appears to be a lambda log_stream, substituting Lambda pattern {} for {}", + LAMBDA_PATTERN, effectiveTableName); + effectiveTableName = effectiveTableName.replace(LAMBDA_PATTERN, LAMBDA_ACTUAL_PATTERN); + } + + DescribeLogStreamsRequest request = new DescribeLogStreamsRequest(logGroup) + .withLogStreamNamePrefix(effectiveTableName); + DescribeLogStreamsResult result = invoker.invoke(() -> awsLogs.describeLogStreams(request)); + for (LogStream nextStream : result.getLogStreams()) { + String logStreamName = nextStream.getLogStreamName(); + CloudwatchTableName nextCloudwatch = new CloudwatchTableName(logGroup, logStreamName); + if (nextCloudwatch.getLogStreamName().equalsIgnoreCase(logStreamName)) { + logger.info("loadLogStream: Matched {} for {}:{}", nextCloudwatch, logGroup, logStream); + return nextCloudwatch; + } + } + + return null; + } + + /** + * Loads the requested LogGroup as identified by the schemaName. + * + * @param schemaName The schemaName to load. + * @return The actual LogGroup name in cloudwatch. + * @note This method also primes the cache with other LogGroups found along the way while scanning Cloudwatch. + */ + private String loadLogGroups(String schemaName) + throws TimeoutException + { + //As an optimization, see if the table name is an exact match (meaning likely no casing issues) + String result = loadLogGroup(schemaName); + if (result != null) { + return result; + } + + logger.info("loadLogGroups: Did not find a match for the schema, falling back to LogGroup scan for {}", schemaName); + DescribeLogGroupsRequest validateSchemaRequest = new DescribeLogGroupsRequest(); + DescribeLogGroupsResult validateSchemaResult; + do { + validateSchemaResult = invoker.invoke(() -> awsLogs.describeLogGroups(validateSchemaRequest)); + for (LogGroup next : validateSchemaResult.getLogGroups()) { + String nextLogGroupName = next.getLogGroupName(); + schemaCache.put(schemaName.toLowerCase(), nextLogGroupName); + if (nextLogGroupName.equalsIgnoreCase(schemaName)) { + logger.info("loadLogGroups: Matched {} for {}", nextLogGroupName, schemaName); + return nextLogGroupName; + } + } + validateSchemaRequest.setNextToken(validateSchemaResult.getNextToken()); + } + while (validateSchemaResult.getNextToken() != null); + + //We could not find a match + throw new IllegalArgumentException("No such schema " + schemaName); + } + + /** + * Optomizaiton that attempts to load a specific LogStream as identified by the TableName. + * + * @param schemaName The schemaName to load. + * @return The CloudwatchTableName or null if not found. + */ + private String loadLogGroup(String schemaName) + throws TimeoutException + { + DescribeLogGroupsRequest request = new DescribeLogGroupsRequest().withLogGroupNamePrefix(schemaName); + DescribeLogGroupsResult result = invoker.invoke(() -> awsLogs.describeLogGroups(request)); + for (LogGroup next : result.getLogGroups()) { + String nextLogGroupName = next.getLogGroupName(); + if (nextLogGroupName.equalsIgnoreCase(schemaName)) { + logger.info("loadLogGroup: Matched {} for {}", nextLogGroupName, schemaName); + return nextLogGroupName; + } + } + + return null; + } + + /** + * Used to validate and convert the given TableName to a properly cased and qualified CloudwatchTableName. + * + * @param tableName The TableName to validate and convert. + * @return The CloudwatchTableName for the provided TableName or throws if the TableName could not be resolved to a + * CloudwatchTableName. This method mostly handles resolving case mismatches and ensuring the input is a valid entity + * in Cloudwatch. + */ + public CloudwatchTableName validateTable(TableName tableName) + { + String actualSchema = validateSchema(tableName.getSchemaName()); + CloudwatchTableName actual = null; + try { + actual = tableCache.get(new TableName(actualSchema, tableName.getTableName())); + if (actual == null) { + throw new IllegalArgumentException("Unknown table[" + tableName + "]"); + } + + return actual; + } + catch (ExecutionException ex) { + throw new RuntimeException("Exception while attempting to validate table " + tableName, ex); + } + } + + /** + * Used to validate and convert the given schema name to a properly cased and qualified CloudwatchTableName. + * + * @param schema The TableName to validate and convert. + * @return The cloudwatch LogGroup (aka schema name) or throws if the schema name could not be resolved to a + * LogGroup. This method mostly handles resolving case mismatches and ensuring the input is a valid entity + * in Cloudwatch. + */ + public String validateSchema(String schema) + { + String actual = null; + try { + actual = schemaCache.get(schema); + if (actual == null) { + throw new IllegalArgumentException("Unknown schema[" + schema + "]"); + } + + return actual; + } + catch (ExecutionException ex) { + throw new RuntimeException("Exception while attempting to validate schema " + schema, ex); + } + } +} diff --git a/athena-cloudwatch/src/test/java/com/amazonaws/athena/connectors/cloudwatch/CloudwatchMetadataHandlerTest.java b/athena-cloudwatch/src/test/java/com/amazonaws/athena/connectors/cloudwatch/CloudwatchMetadataHandlerTest.java new file mode 100644 index 0000000000..a9e7ef1671 --- /dev/null +++ b/athena-cloudwatch/src/test/java/com/amazonaws/athena/connectors/cloudwatch/CloudwatchMetadataHandlerTest.java @@ -0,0 +1,409 @@ +/*- + * #%L + * athena-cloudwatch + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.cloudwatch; + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.amazonaws.athena.connector.lambda.data.BlockUtils; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.domain.predicate.EquatableValueSet; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesResponse; +import com.amazonaws.athena.connector.lambda.metadata.MetadataRequestType; +import com.amazonaws.athena.connector.lambda.metadata.MetadataResponse; +import com.amazonaws.athena.connector.lambda.security.FederatedIdentity; +import com.amazonaws.athena.connector.lambda.security.LocalKeyFactory; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.logs.AWSLogs; +import com.amazonaws.services.logs.model.DescribeLogGroupsRequest; +import com.amazonaws.services.logs.model.DescribeLogGroupsResult; +import com.amazonaws.services.logs.model.DescribeLogStreamsRequest; +import com.amazonaws.services.logs.model.DescribeLogStreamsResult; +import com.amazonaws.services.logs.model.LogGroup; +import com.amazonaws.services.logs.model.LogStream; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.arrow.vector.types.pojo.Schema; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.runners.MockitoJUnitRunner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeoutException; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class CloudwatchMetadataHandlerTest +{ + private static final Logger logger = LoggerFactory.getLogger(CloudwatchMetadataHandlerTest.class); + + private FederatedIdentity identity = new FederatedIdentity("id", "principal", "account"); + private CloudwatchMetadataHandler handler; + private BlockAllocator allocator; + + @Mock + private AWSLogs mockAwsLogs; + + @Mock + private AWSSecretsManager mockSecretsManager; + + @Mock + private AmazonAthena mockAthena; + + @Before + public void setUp() + throws Exception + { + when(mockAwsLogs.describeLogStreams(any(DescribeLogStreamsRequest.class))).thenAnswer((InvocationOnMock invocationOnMock) -> { + return new DescribeLogStreamsResult().withLogStreams(new LogStream().withLogStreamName("table-9"), + new LogStream().withLogStreamName("table-10")); + }); + + when(mockAwsLogs.describeLogGroups(any(DescribeLogGroupsRequest.class))).thenAnswer((InvocationOnMock invocationOnMock) -> { + return new DescribeLogGroupsResult().withLogGroups(new LogGroup().withLogGroupName("schema-1"), + new LogGroup().withLogGroupName("schema-20")); + }); + handler = new CloudwatchMetadataHandler(mockAwsLogs, new LocalKeyFactory(), mockSecretsManager, mockAthena, "spillBucket", "spillPrefix"); + allocator = new BlockAllocatorImpl(); + } + + @After + public void tearDown() + throws Exception + { + allocator.close(); + } + + @Test + public void doListSchemaNames() + throws TimeoutException + { + logger.info("doListSchemas - enter"); + + when(mockAwsLogs.describeLogGroups(any(DescribeLogGroupsRequest.class))).thenAnswer((InvocationOnMock invocationOnMock) -> { + DescribeLogGroupsRequest request = (DescribeLogGroupsRequest) invocationOnMock.getArguments()[0]; + + DescribeLogGroupsResult result = new DescribeLogGroupsResult(); + + Integer nextToken; + if (request.getNextToken() == null) { + nextToken = 1; + } + else if (Integer.valueOf(request.getNextToken()) < 3) { + nextToken = Integer.valueOf(request.getNextToken()) + 1; + } + else { + nextToken = null; + } + + List logGroups = new ArrayList<>(); + if (request.getNextToken() == null || Integer.valueOf(request.getNextToken()) < 3) { + for (int i = 0; i < 10; i++) { + LogGroup nextLogGroup = new LogGroup(); + nextLogGroup.setLogGroupName("schema-" + String.valueOf(i)); + logGroups.add(nextLogGroup); + } + } + + result.withLogGroups(logGroups); + if (nextToken != null) { + result.setNextToken(String.valueOf(nextToken)); + } + + return result; + }); + + ListSchemasRequest req = new ListSchemasRequest(identity, "queryId", "default"); + ListSchemasResponse res = handler.doListSchemaNames(allocator, req); + logger.info("doListSchemas - {}", res.getSchemas()); + + assertTrue(res.getSchemas().size() == 30); + verify(mockAwsLogs, times(4)).describeLogGroups(any(DescribeLogGroupsRequest.class)); + verifyNoMoreInteractions(mockAwsLogs); + + logger.info("doListSchemas - exit"); + } + + @Test + public void doListTables() + throws TimeoutException + { + logger.info("doListTables - enter"); + + when(mockAwsLogs.describeLogStreams(any(DescribeLogStreamsRequest.class))).thenAnswer((InvocationOnMock invocationOnMock) -> { + DescribeLogStreamsRequest request = (DescribeLogStreamsRequest) invocationOnMock.getArguments()[0]; + + DescribeLogStreamsResult result = new DescribeLogStreamsResult(); + + Integer nextToken; + if (request.getNextToken() == null) { + nextToken = 1; + } + else if (Integer.valueOf(request.getNextToken()) < 3) { + nextToken = Integer.valueOf(request.getNextToken()) + 1; + } + else { + nextToken = null; + } + + List logStreams = new ArrayList<>(); + if (request.getNextToken() == null || Integer.valueOf(request.getNextToken()) < 3) { + for (int i = 0; i < 10; i++) { + LogStream nextLogStream = new LogStream(); + nextLogStream.setLogStreamName("table-" + String.valueOf(i)); + logStreams.add(nextLogStream); + } + } + + result.withLogStreams(logStreams); + if (nextToken != null) { + result.setNextToken(String.valueOf(nextToken)); + } + + return result; + }); + + ListTablesRequest req = new ListTablesRequest(identity, "queryId", "default", "schema-1"); + ListTablesResponse res = handler.doListTables(allocator, req); + logger.info("doListTables - {}", res.getTables()); + + assertTrue(res.getTables().contains(new TableName("schema-1", "all_log_streams"))); + + assertTrue(res.getTables().size() == 31); + + verify(mockAwsLogs, times(4)).describeLogStreams(any(DescribeLogStreamsRequest.class)); + verify(mockAwsLogs, times(1)).describeLogGroups(any(DescribeLogGroupsRequest.class)); + verifyNoMoreInteractions(mockAwsLogs); + + logger.info("doListTables - exit"); + } + + @Test + public void doGetTable() + { + logger.info("doGetTable - enter"); + String expectedSchema = "schema-20"; + + when(mockAwsLogs.describeLogStreams(any(DescribeLogStreamsRequest.class))).thenAnswer((InvocationOnMock invocationOnMock) -> { + DescribeLogStreamsRequest request = (DescribeLogStreamsRequest) invocationOnMock.getArguments()[0]; + + assertTrue(request.getLogGroupName().equals(expectedSchema)); + DescribeLogStreamsResult result = new DescribeLogStreamsResult(); + + Integer nextToken; + if (request.getNextToken() == null) { + nextToken = 1; + } + else if (Integer.valueOf(request.getNextToken()) < 3) { + nextToken = Integer.valueOf(request.getNextToken()) + 1; + } + else { + nextToken = null; + } + + List logStreams = new ArrayList<>(); + if (request.getNextToken() == null || Integer.valueOf(request.getNextToken()) < 3) { + for (int i = 0; i < 10; i++) { + LogStream nextLogStream = new LogStream(); + nextLogStream.setLogStreamName("table-" + String.valueOf(i)); + logStreams.add(nextLogStream); + } + } + + result.withLogStreams(logStreams); + if (nextToken != null) { + result.setNextToken(String.valueOf(nextToken)); + } + + return result; + }); + + GetTableRequest req = new GetTableRequest(identity, "queryId", "default", new TableName(expectedSchema, "table-9")); + GetTableResponse res = handler.doGetTable(allocator, req); + logger.info("doGetTable - {} {}", res.getTableName(), res.getSchema()); + + assertEquals(new TableName(expectedSchema, "table-9"), res.getTableName()); + assertTrue(res.getSchema() != null); + + verify(mockAwsLogs, times(1)).describeLogStreams(any(DescribeLogStreamsRequest.class)); + + logger.info("doGetTable - exit"); + } + + @Test + public void doGetTableLayout() + throws Exception + { + logger.info("doGetTableLayout - enter"); + + when(mockAwsLogs.describeLogStreams(any(DescribeLogStreamsRequest.class))).thenAnswer((InvocationOnMock invocationOnMock) -> { + DescribeLogStreamsRequest request = (DescribeLogStreamsRequest) invocationOnMock.getArguments()[0]; + + DescribeLogStreamsResult result = new DescribeLogStreamsResult(); + + Integer nextToken; + if (request.getNextToken() == null) { + nextToken = 1; + } + else if (Integer.valueOf(request.getNextToken()) < 3) { + nextToken = Integer.valueOf(request.getNextToken()) + 1; + } + else { + nextToken = null; + } + + List logStreams = new ArrayList<>(); + if (request.getNextToken() == null || Integer.valueOf(request.getNextToken()) < 3) { + int continuation = request.getNextToken() == null ? 0 : Integer.valueOf(request.getNextToken()); + for (int i = 0 + continuation * 100; i < 300; i++) { + LogStream nextLogStream = new LogStream(); + nextLogStream.setLogStreamName("table-" + String.valueOf(i)); + nextLogStream.setStoredBytes(i * 1000L); + logStreams.add(nextLogStream); + } + } + + result.withLogStreams(logStreams); + if (nextToken != null) { + result.setNextToken(String.valueOf(nextToken)); + } + + return result; + }); + + Map constraintsMap = new HashMap<>(); + + constraintsMap.put("log_stream", + EquatableValueSet.newBuilder(allocator, Types.MinorType.VARCHAR.getType(), true, false) + .add("table-10").build()); + + Schema schema = SchemaBuilder.newBuilder().addStringField("log_stream").build(); + + GetTableLayoutRequest req = new GetTableLayoutRequest(identity, + "queryId", + "default", + new TableName("schema-1", "all_log_streams"), + new Constraints(constraintsMap), + schema, + Collections.singleton("log_stream")); + + GetTableLayoutResponse res = handler.doGetTableLayout(allocator, req); + + logger.info("doGetTableLayout - {}", res.getPartitions().getSchema()); + logger.info("doGetTableLayout - {}", res.getPartitions()); + + assertTrue(res.getPartitions().getSchema().findField("log_stream") != null); + assertTrue(res.getPartitions().getRowCount() == 1); + + verify(mockAwsLogs, times(4)).describeLogStreams(any(DescribeLogStreamsRequest.class)); + + logger.info("doGetTableLayout - exit"); + } + + @Test + public void doGetSplits() + { + logger.info("doGetSplits: enter"); + + Schema schema = SchemaBuilder.newBuilder() + .addField(CloudwatchMetadataHandler.LOG_STREAM_FIELD, new ArrowType.Utf8()) + .addField(CloudwatchMetadataHandler.LOG_STREAM_SIZE_FIELD, new ArrowType.Int(64, true)) + .addField(CloudwatchMetadataHandler.LOG_GROUP_FIELD, new ArrowType.Utf8()) + .build(); + + Block partitions = allocator.createBlock(schema); + + int num_partitions = 2_000; + for (int i = 0; i < num_partitions; i++) { + BlockUtils.setValue(partitions.getFieldVector(CloudwatchMetadataHandler.LOG_STREAM_SIZE_FIELD), i, 2016L + i); + BlockUtils.setValue(partitions.getFieldVector(CloudwatchMetadataHandler.LOG_STREAM_FIELD), i, "log_stream_" + i); + BlockUtils.setValue(partitions.getFieldVector(CloudwatchMetadataHandler.LOG_GROUP_FIELD), i, "log_group_" + i); + } + partitions.setRowCount(num_partitions); + + String continuationToken = null; + GetSplitsRequest originalReq = new GetSplitsRequest(identity, + "queryId", + "catalog_name", + new TableName("schema", "all_log_streams"), + partitions, + Collections.singletonList(CloudwatchMetadataHandler.LOG_STREAM_FIELD), + new Constraints(new HashMap<>()), + continuationToken); + int numContinuations = 0; + do { + GetSplitsRequest req = new GetSplitsRequest(originalReq, continuationToken); + logger.info("doGetSplits: req[{}]", req); + + MetadataResponse rawResponse = handler.doGetSplits(allocator, req); + assertEquals(MetadataRequestType.GET_SPLITS, rawResponse.getRequestType()); + + GetSplitsResponse response = (GetSplitsResponse) rawResponse; + continuationToken = response.getContinuationToken(); + + logger.info("doGetSplits: continuationToken[{}] - numSplits[{}]", continuationToken, response.getSplits().size()); + + for (Split nextSplit : response.getSplits()) { + assertNotNull(nextSplit.getProperty(CloudwatchMetadataHandler.LOG_STREAM_SIZE_FIELD)); + assertNotNull(nextSplit.getProperty(CloudwatchMetadataHandler.LOG_STREAM_FIELD)); + assertNotNull(nextSplit.getProperty(CloudwatchMetadataHandler.LOG_GROUP_FIELD)); + } + + if (continuationToken != null) { + numContinuations++; + } + } + while (continuationToken != null); + + assertTrue(numContinuations > 0); + + logger.info("doGetSplits: exit"); + } +} diff --git a/athena-cloudwatch/src/test/java/com/amazonaws/athena/connectors/cloudwatch/CloudwatchRecordHandlerTest.java b/athena-cloudwatch/src/test/java/com/amazonaws/athena/connectors/cloudwatch/CloudwatchRecordHandlerTest.java new file mode 100644 index 0000000000..0f5e82043b --- /dev/null +++ b/athena-cloudwatch/src/test/java/com/amazonaws/athena/connectors/cloudwatch/CloudwatchRecordHandlerTest.java @@ -0,0 +1,293 @@ +/*- + * #%L + * athena-cloudwatch + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.cloudwatch; + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.amazonaws.athena.connector.lambda.data.BlockUtils; +import com.amazonaws.athena.connector.lambda.data.S3BlockSpillReader; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.domain.predicate.Range; +import com.amazonaws.athena.connector.lambda.domain.predicate.SortedRangeSet; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import com.amazonaws.athena.connector.lambda.domain.spill.S3SpillLocation; +import com.amazonaws.athena.connector.lambda.domain.spill.SpillLocation; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsResponse; +import com.amazonaws.athena.connector.lambda.records.RecordResponse; +import com.amazonaws.athena.connector.lambda.records.RemoteReadRecordsResponse; +import com.amazonaws.athena.connector.lambda.security.EncryptionKeyFactory; +import com.amazonaws.athena.connector.lambda.security.FederatedIdentity; +import com.amazonaws.athena.connector.lambda.security.LocalKeyFactory; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.logs.AWSLogs; +import com.amazonaws.services.logs.model.GetLogEventsRequest; +import com.amazonaws.services.logs.model.GetLogEventsResult; +import com.amazonaws.services.logs.model.OutputLogEvent; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.PutObjectResult; +import com.amazonaws.services.s3.model.S3Object; +import com.amazonaws.services.s3.model.S3ObjectInputStream; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.google.common.collect.ImmutableList; +import com.google.common.io.ByteStreams; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Schema; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.runners.MockitoJUnitRunner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class CloudwatchRecordHandlerTest +{ + private static final Logger logger = LoggerFactory.getLogger(CloudwatchRecordHandlerTest.class); + + private FederatedIdentity identity = new FederatedIdentity("id", "principal", "account"); + private List mockS3Storage; + private CloudwatchRecordHandler handler; + private S3BlockSpillReader spillReader; + private BlockAllocator allocator; + private Schema schemaForRead; + private EncryptionKeyFactory keyFactory = new LocalKeyFactory(); + + @Mock + private AWSLogs mockAwsLogs; + + @Mock + private AmazonS3 mockS3; + + @Mock + private AWSSecretsManager mockSecretsManager; + + @Mock + private AmazonAthena mockAthena; + + @Before + public void setUp() + throws Exception + { + schemaForRead = CloudwatchMetadataHandler.CLOUDWATCH_SCHEMA; + + mockS3Storage = new ArrayList<>(); + allocator = new BlockAllocatorImpl(); + handler = new CloudwatchRecordHandler(mockS3, mockSecretsManager, mockAthena, mockAwsLogs); + spillReader = new S3BlockSpillReader(mockS3, allocator); + + when(mockS3.putObject(anyObject(), anyObject(), anyObject(), anyObject())) + .thenAnswer((InvocationOnMock invocationOnMock) -> + { + InputStream inputStream = (InputStream) invocationOnMock.getArguments()[2]; + ByteHolder byteHolder = new ByteHolder(); + byteHolder.setBytes(ByteStreams.toByteArray(inputStream)); + mockS3Storage.add(byteHolder); + return mock(PutObjectResult.class); + }); + + when(mockS3.getObject(anyString(), anyString())) + .thenAnswer((InvocationOnMock invocationOnMock) -> + { + S3Object mockObject = mock(S3Object.class); + ByteHolder byteHolder = mockS3Storage.get(0); + mockS3Storage.remove(0); + when(mockObject.getObjectContent()).thenReturn( + new S3ObjectInputStream( + new ByteArrayInputStream(byteHolder.getBytes()), null)); + return mockObject; + }); + + when(mockAwsLogs.getLogEvents(any(GetLogEventsRequest.class))).thenAnswer((InvocationOnMock invocationOnMock) -> { + GetLogEventsRequest request = (GetLogEventsRequest) invocationOnMock.getArguments()[0]; + + //Check that predicate pushdown was propagated to cloudwatch + assertNotNull(request.getStartTime()); + assertNotNull(request.getEndTime()); + + GetLogEventsResult result = new GetLogEventsResult(); + + Integer nextToken; + if (request.getNextToken() == null) { + nextToken = 1; + } + else if (Integer.valueOf(request.getNextToken()) < 3) { + nextToken = Integer.valueOf(request.getNextToken()) + 1; + } + else { + nextToken = null; + } + + List logEvents = new ArrayList<>(); + if (request.getNextToken() == null || Integer.valueOf(request.getNextToken()) < 3) { + long continuation = request.getNextToken() == null ? 0 : Integer.valueOf(request.getNextToken()); + for (int i = 0; i < 100_000; i++) { + OutputLogEvent outputLogEvent = new OutputLogEvent(); + outputLogEvent.setMessage("message-" + (continuation * i)); + outputLogEvent.setTimestamp(i * 100L); + logEvents.add(outputLogEvent); + } + } + + result.withEvents(logEvents); + if (nextToken != null) { + result.setNextForwardToken(String.valueOf(nextToken)); + } + + return result; + }); + } + + @After + public void tearDown() + throws Exception + { + allocator.close(); + } + + @Test + public void doReadRecordsNoSpill() + throws Exception + { + logger.info("doReadRecordsNoSpill: enter"); + + Map constraintsMap = new HashMap<>(); + constraintsMap.put("time", SortedRangeSet.copyOf(Types.MinorType.BIGINT.getType(), + ImmutableList.of(Range.equal(allocator, Types.MinorType.BIGINT.getType(), 100L)), false)); + + ReadRecordsRequest request = new ReadRecordsRequest(identity, + "catalog", + "queryId-" + System.currentTimeMillis(), + new TableName("schema", "table"), + schemaForRead, + Split.newBuilder(S3SpillLocation.newBuilder() + .withBucket(UUID.randomUUID().toString()) + .withSplitId(UUID.randomUUID().toString()) + .withQueryId(UUID.randomUUID().toString()) + .withIsDirectory(true) + .build(), + keyFactory.create()).add(CloudwatchMetadataHandler.LOG_STREAM_FIELD, "table").build(), + new Constraints(constraintsMap), + 100_000_000_000L, + 100_000_000_000L//100GB don't expect this to spill + ); + + RecordResponse rawResponse = handler.doReadRecords(allocator, request); + + assertTrue(rawResponse instanceof ReadRecordsResponse); + + ReadRecordsResponse response = (ReadRecordsResponse) rawResponse; + logger.info("doReadRecordsNoSpill: rows[{}]", response.getRecordCount()); + + assertTrue(response.getRecords().getRowCount() == 3); + logger.info("doReadRecordsNoSpill: {}", BlockUtils.rowToString(response.getRecords(), 0)); + + logger.info("doReadRecordsNoSpill: exit"); + } + + @Test + public void doReadRecordsSpill() + throws Exception + { + logger.info("doReadRecordsSpill: enter"); + + Map constraintsMap = new HashMap<>(); + constraintsMap.put("time", SortedRangeSet.of( + Range.range(allocator, Types.MinorType.BIGINT.getType(), 100L, true, 100_000_000L, true))); + + ReadRecordsRequest request = new ReadRecordsRequest(identity, + "catalog", + "queryId-" + System.currentTimeMillis(), + new TableName("schema", "table"), + schemaForRead, + Split.newBuilder(S3SpillLocation.newBuilder() + .withBucket(UUID.randomUUID().toString()) + .withSplitId(UUID.randomUUID().toString()) + .withQueryId(UUID.randomUUID().toString()) + .withIsDirectory(true) + .build(), + keyFactory.create()).add(CloudwatchMetadataHandler.LOG_STREAM_FIELD, "table").build(), + new Constraints(constraintsMap), + 1_500_000L, //~1.5MB so we should see some spill + 0 + ); + + RecordResponse rawResponse = handler.doReadRecords(allocator, request); + + assertTrue(rawResponse instanceof RemoteReadRecordsResponse); + + try (RemoteReadRecordsResponse response = (RemoteReadRecordsResponse) rawResponse) { + logger.info("doReadRecordsSpill: remoteBlocks[{}]", response.getRemoteBlocks().size()); + + assertTrue(response.getNumberBlocks() > 1); + + int blockNum = 0; + for (SpillLocation next : response.getRemoteBlocks()) { + S3SpillLocation spillLocation = (S3SpillLocation) next; + try (Block block = spillReader.read(spillLocation, response.getEncryptionKey(), response.getSchema())) { + + logger.info("doReadRecordsSpill: blockNum[{}] and recordCount[{}]", blockNum++, block.getRowCount()); + // assertTrue(++blockNum < response.getRemoteBlocks().size() && block.getRowCount() > 10_000); + + logger.info("doReadRecordsSpill: {}", BlockUtils.rowToString(block, 0)); + assertNotNull(BlockUtils.rowToString(block, 0)); + } + } + } + + logger.info("doReadRecordsSpill: exit"); + } + + private class ByteHolder + { + private byte[] bytes; + + public void setBytes(byte[] bytes) + { + this.bytes = bytes; + } + + public byte[] getBytes() + { + return bytes; + } + } +} diff --git a/athena-docdb/LICENSE.txt b/athena-docdb/LICENSE.txt new file mode 100644 index 0000000000..418de4c108 --- /dev/null +++ b/athena-docdb/LICENSE.txt @@ -0,0 +1,174 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. \ No newline at end of file diff --git a/athena-docdb/README.md b/athena-docdb/README.md new file mode 100644 index 0000000000..04268f5a62 --- /dev/null +++ b/athena-docdb/README.md @@ -0,0 +1,95 @@ +# Amazon Athena DocumentDB Connector + +This connector enables Amazon Athena to communicate with your DocumentDB instance(s), making your DocumentDB data accessible via SQL. The also works with any MongoDB compatible endpoint. + +Unlike traditional relational data stores, DocumentDB collections do not have set schema. Each entry can have different fields and data types. While we are investigating the best way to support schema-on-read usecases for this connector, it presently supports two mechanisms for generating traditional table schema information. The default mechanism is for the connector to scan a small number of documents in your collection in order to form a union of all fields and coerce fields with non-overlap data types. This basic schema inference works well for collections that have mostly uniform entries. For more diverse collections, the connector supports retrieving meta-data from the Glue Data Catalog. If the connector sees a database and table which match your DocumentDB database and collection names it will use the corresponding Glue table for schema. We recommend creating your Glue table such that it is a superset of all fields you may want to access from your DocumentDB Collection. + +### Parameters + +The Amazon Athena DocumentDB Connector exposes several configuration options via Lambda environment variables. More detail on the available parameters can be found below. + +1. **spill_bucket** - When the data returned by your Lambda function exceeds Lambda’s limits, this is the bucket that the data will be written to for Athena to read the excess from. (e.g. my_bucket) +2. **spill_prefix** - (Optional) Defaults to sub-folder in your bucket called 'athena-federation-spill'. Used in conjunction with spill_bucket, this is the path within the above bucket that large responses are spilled to. You should configure an S3 lifecycle on this location to delete old spills after X days/Hours. +3. **kms_key_id** - (Optional) By default any data that is spilled to S3 is encrypted using AES-GCM and a randomly generated key. Setting a KMS Key ID allows your Lambda function to use KMS for key generation for a stronger source of encryption keys. (e.g. a7e63k4b-8loc-40db-a2a1-4d0en2cd8331) +4. **disable_spill_encryption** - (Optional) Defaults to False so that any data that is spilled to S3 is encrypted using AES-GMC either with a randomly generated key or using KMS to generate keys. Setting this to false will disable spill encryption. You may wish to disable this for improved performance, especially if your spill location in S3 uses S3 Server Side Encryption. (e.g. True or False) +5. **disable_glue** - (Optional) If present, with any valye, the connector will no longer attempt to retrieve supplemental metadata from Glue. +6. **glue_catalog** - (Optional) Can be used to target a cross-account Glue catalog. By default the connector will attempt to get metadata from its own Glue account. +7. **default_docdb** If present, this DocDB connection string is used when there is not a catalog specific environment variable (as explained below). (e.g. mongodb://:@:/?ssl=true&ssl_ca_certs=rds-combined-ca-bundle.pem&replicaSet=rs0) + +You can also provide one or more properties which define the DocumentDB connection details for the DocumentDB instance(s) you'd like this connector to use. You can do this by setting a Lambda environment variable that corresponds to the catalog name you'd like to use in Athena. For example, if I'd like to query two different DocumentDB instances from Athena in the below queries: + +```sql + select * from "docdb_instance_1".database.table + select * from "docdb_instance_2".database.table + ``` + +To support these two SQL statements we'd need to add two environment variables to our Lambda function: + +1. **docdb_instance_1** - The value should be the DocumentDB connection details in the format of:mongodb://:@:/?ssl=true&ssl_ca_certs=rds-combined-ca-bundle.pem&replicaSet=rs0 +2. **docdb_instance_2** - The value should be the DocumentDB connection details in the format of: mongodb://:@:/?ssl=true&ssl_ca_certs=rds-combined-ca-bundle.pem&replicaSet=rs0 + +You can also optionally use SecretsManager for part or all of the value for the preceeding connection details. For example, if I set a Lambda environment variable for **docdb_instance_1** to be "mongodb://${docdb_instance_1_creds}@myhostname.com:123/?ssl=true&ssl_ca_certs=rds-combined-ca-bundle.pem&replicaSet=rs0" the Athena Federation +SDK will automatically attempt to retrieve a secret from AWS SecretsManager named "docdb_instance_1_creds" and inject that value in place of "${docdb_instance_1_creds}". Basically anything between ${...} is attempted as a secret in SecretsManager. If no such secret exists, the text isn't replaced. + + +### Setting Up Databases & Tables + +To enable a Glue Table for use with DocumentDB, you simply need to have a Glue database and table that matches any DocumentDB Database and Collection that you'd like to supply supplemental metadata for (instead of relying on the DocumentDB Connector's ability to infer schema). The connector's in built schema inference only supports a subset of data types and scans a limited number of documents. You can enable a Glue table to be used for supplemental metadata by setting the below table properties from the Glue Console when editing the Table and databse in question. The only other thing you need to do ensure you use the appropriate data types listed in a later section. + +1. **docdb-metadata-flag** - Flag indicating that the table can be used for supplemental meta-data by the Athena DocDB Connector. The value is unimportant as long as this key is present in the properties of the table. + +### Data Types + +The schema inference feature of this connector will attempt to infer values as one of the following: + +|Apache Arrow DataType|Java/DocDB Type| +|-------------|-----------------| +|VARCHAR|String| +|INT|Integer| +|BIGINT|Long| +|BIT|Boolean| +|FLOAT4|Float| +|FLOAT8|Double| +|TIMESTAMPSEC|Date| +|VARCHAR|ObjectId| +|LIST|List| +|STRUCT|Document| + +Alternatively, if you are using Glue for supplimental metadata you can configure the following types: + +|Glue DataType|Apache Arrow Type| +|-------------|-----------------| +|int|INT| +|bigint|BIGINT| +|double|FLOAT8| +|float|FLOAT4| +|boolean|BIT| +|binary|VARBINARY| +|string|VARCHAR| +|List|LIST| +|Struct|STRUCT| + +### Required Permissions + +Review the "Policies" section of the athena-docdb.yaml file for full details on the IAM Policies required by this connector. A brief summary is below. + +1. S3 Write Access - In order to successfully handle large queries, the connector requires write access to a location in S3. +2. SecretsManager Read Access - If you choose to store redis-endpoint details in SecretsManager you will need to grant the connector access to those secrets. +3. Glue Data Catalog - Since DocumentDB does not have a meta-data store, the connector requires Read-Only access to Glue's DataCatalog for supplemental table schema information. +4. VPC Access - In order to connect to your VPC for the purposes of communicating with your DocumentDB instance(s), the connector needs the ability to attach/detach an interface to the VPC. +5. CloudWatch Logs - This is a somewhat implicit permission when deploying a Lambda function but it needs access to cloudwatch logs for storing logs. +1. Athena GetQueryExecution - The connector uses this access to fast-fail when the upstream Athena query has terminated. + +### Deploying The Connector + +To use this connector in your queries, navigate to AWS Serverless Application Repository and deploy a pre-built version of this connector. Alternatively, you can build and deploy this connector from source follow the below steps or use the more detailed tutorial in the athena-example module: + +1. From the athena-federation-sdk dir, run `mvn clean install` if you haven't already. +2. From the athena-docdb dir, run `mvn clean install`. +3. From the athena-docdb dir, run `../tools/publish.sh S3_BUCKET_NAME athena-docdb` to publish the connector to your private AWS Serverless Application Repository. The S3_BUCKET in the command is where a copy of the connector's code will be stored for Serverless Application Repository to retrieve it. This will allow users with permission to do so, the ability to deploy instances of the connector via 1-Click form. Then navigate to [Serverless Application Repository](https://aws.amazon.com/serverless/serverlessrepo) + + +## Performance + +The Athena DocumentDB Connector does not current support parallel scans but will attempt to push down predicates as part of its DocumentDB queries. + diff --git a/athena-docdb/athena-docdb.yaml b/athena-docdb/athena-docdb.yaml new file mode 100644 index 0000000000..49b3d2e1c3 --- /dev/null +++ b/athena-docdb/athena-docdb.yaml @@ -0,0 +1,98 @@ +Transform: 'AWS::Serverless-2016-10-31' +Metadata: + 'AWS::ServerlessRepo::Application': + Name: AthenaDocumentDBConnector + Description: This connector enables Amazon Athena to communicate with your DocumentDB instance(s), making your DocumentDB data accessible via SQL. + Author: 'Amazon Athena' + SpdxLicenseId: Apache-2.0 + LicenseUrl: LICENSE.txt + ReadmeUrl: README.md + Labels: + - athena-federation + HomePageUrl: 'https://github.com/awslabs/aws-athena-query-federation' + SemanticVersion: 1.0.2 + SourceCodeUrl: 'https://github.com/awslabs/aws-athena-query-federation' +Parameters: + AthenaCatalogName: + Description: 'The name you will give to this catalog in Athena. It will also be used as the function name.' + Type: String + SpillBucket: + Description: 'The bucket where this function can spill data.' + Type: String + Default: athena-federation-spill + SpillPrefix: + Description: 'The bucket prefix where this function can spill large responses.' + Type: String + Default: athena-spill + LambdaTimeout: + Description: 'Maximum Lambda invocation runtime in seconds. (min 1 - 900 max)' + Default: 900 + Type: Number + LambdaMemory: + Description: 'Lambda memory in MB (min 128 - 3008 max).' + Default: 3008 + Type: Number + DisableSpillEncryption: + Description: 'If set to ''false'' data spilled to S3 is encrypted with AES GCM' + Default: 'false' + Type: String + SecurityGroupIds: + Description: 'One or more SecurityGroup IDs corresponding to the SecurityGroup that should be applied to the Lambda function. (e.g. sg1,sg2,sg3)' + Type: 'List' + SubnetIds: + Description: 'One or more Subnet IDs corresponding to the Subnet that the Lambda function can use to access you data source. (e.g. subnet1,subnet2)' + Type: 'List' + SecretNameOrPrefix: + Description: 'The name or prefix of a set of names within Secrets Manager that this function should have access to. (e.g. hbase-*).' + Type: String + DocDBConnectionString: + Description: 'The DocDB connection details to use by default if not catalog specific connection is defined and optionally using SecretsManager (e.g. ${secret_name}).' + Type: String + Default: "e.g. mongodb://:@:/?ssl=true&ssl_ca_certs=rds-combined-ca-bundle.pem&replicaSet=rs0" +Resources: + ConnectorConfig: + Type: 'AWS::Serverless::Function' + Properties: + Environment: + Variables: + disable_spill_encryption: !Ref DisableSpillEncryption + spill_bucket: !Ref SpillBucket + spill_prefix: !Ref SpillPrefix + default_docdb: !Ref DocDBConnectionString + FunctionName: !Ref AthenaCatalogName + Handler: "com.amazonaws.athena.connectors.docdb.DocDBCompositeHandler" + CodeUri: "./target/athena-docdb-1.0.jar" + Description: "Enables Amazon Athena to communicate with DocumentDB, making your DocumentDB data accessible via SQL." + Runtime: java8 + Timeout: !Ref LambdaTimeout + MemorySize: !Ref LambdaMemory + Policies: + - Statement: + - Action: + - secretsmanager:GetSecretValue + Effect: Allow + Resource: !Sub 'arn:aws:secretsmanager:*:*:secret:${SecretNameOrPrefix}' + Version: '2012-10-17' + - Statement: + - Action: + - glue:GetTableVersions + - glue:GetPartitions + - glue:GetTables + - glue:GetTableVersion + - glue:GetDatabases + - glue:GetTable + - glue:GetPartition + - glue:GetDatabase + - athena:GetQueryExecution + Effect: Allow + Resource: '*' + Version: '2012-10-17' + #S3CrudPolicy allows our connector to spill large responses to S3. You can optionally replace this pre-made policy + #with one that is more restrictive and can only 'put' but not read,delete, or overwrite files. + - S3CrudPolicy: + BucketName: !Ref SpillBucket + #VPCAccessPolicy allows our connector to run in a VPC so that it can access your data source. + - VPCAccessPolicy: {} + VpcConfig: + SecurityGroupIds: !Ref SecurityGroupIds + SubnetIds: !Ref SubnetIds \ No newline at end of file diff --git a/athena-docdb/pom.xml b/athena-docdb/pom.xml new file mode 100644 index 0000000000..4efd98b3ae --- /dev/null +++ b/athena-docdb/pom.xml @@ -0,0 +1,57 @@ + + + + aws-athena-query-federation + com.amazonaws + 1.0 + + 4.0.0 + + athena-docdb + + + + com.amazonaws + aws-athena-federation-sdk + ${aws-athena-federation-sdk.version} + + + org.mongodb + mongo-java-driver + 3.10.2 + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.1 + + false + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + package + + shade + + + + + + + \ No newline at end of file diff --git a/athena-docdb/src/main/java/com/amazonaws/athena/connectors/docdb/DocDBCompositeHandler.java b/athena-docdb/src/main/java/com/amazonaws/athena/connectors/docdb/DocDBCompositeHandler.java new file mode 100644 index 0000000000..df5342f7f5 --- /dev/null +++ b/athena-docdb/src/main/java/com/amazonaws/athena/connectors/docdb/DocDBCompositeHandler.java @@ -0,0 +1,35 @@ +/*- + * #%L + * athena-mongodb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.docdb; + +import com.amazonaws.athena.connector.lambda.handlers.CompositeHandler; + +/** + * Boilerplate composite handler that allows us to use a single Lambda function for both + * Metadata and Data. In this case we just compose DocDBMetadataHandler and DocDBRecordHandler. + */ +public class DocDBCompositeHandler + extends CompositeHandler +{ + public DocDBCompositeHandler() + { + super(new DocDBMetadataHandler(), new DocDBRecordHandler()); + } +} diff --git a/athena-docdb/src/main/java/com/amazonaws/athena/connectors/docdb/DocDBConnectionFactory.java b/athena-docdb/src/main/java/com/amazonaws/athena/connectors/docdb/DocDBConnectionFactory.java new file mode 100644 index 0000000000..715f2a4104 --- /dev/null +++ b/athena-docdb/src/main/java/com/amazonaws/athena/connectors/docdb/DocDBConnectionFactory.java @@ -0,0 +1,93 @@ +/*- + * #%L + * athena-mongodb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.docdb; + +import com.mongodb.client.MongoClient; +import com.mongodb.client.MongoClients; +import org.apache.arrow.util.VisibleForTesting; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.Map; + +/** + * Creates and Caches HBase Connection Instances, using the connection string as the cache key. + * + * @Note Connection String format is expected to be like: + * mongodb://:@:/?ssl=true&ssl_ca_certs=&replicaSet= + */ +public class DocDBConnectionFactory +{ + private static final Logger logger = LoggerFactory.getLogger(DocDBConnectionFactory.class); + private final Map clientCache = new HashMap<>(); + + /** + * Used to get an existing, pooled, connection or to create a new connection + * for the given connection string. + * + * @param connStr MongoClient connection details, format is expected to be: + * mongodb://:@:/?ssl=true&ssl_ca_certs=&replicaSet= + * @return A MongoClient connection if the connection succeeded, else the function will throw. + */ + public synchronized MongoClient getOrCreateConn(String connStr) + { + logger.info("getOrCreateConn: enter"); + MongoClient result = clientCache.get(connStr); + + if (result == null || !connectionTest(result)) { + result = MongoClients.create(connStr); + clientCache.put(connStr, result); + } + + logger.info("getOrCreateConn: exit"); + return result; + } + + /** + * Runs a 'quick' test on the connection and then returns it if it passes. + */ + private boolean connectionTest(MongoClient conn) + { + try { + logger.info("connectionTest: Testing connection started."); + conn.listDatabaseNames(); + logger.info("connectionTest: Testing connection completed - success."); + return true; + } + catch (RuntimeException ex) { + logger.warn("getOrCreateConn: Exception while testing existing connection.", ex); + } + logger.info("connectionTest: Testing connection completed - fail."); + return false; + } + + /** + * Injects a connection into the client cache. + * + * @param conStr The connection string (aka the cache key) + * @param conn The connection to inject into the client cache, most often a Mock used in testing. + */ + @VisibleForTesting + protected synchronized void addConnection(String conStr, MongoClient conn) + { + clientCache.put(conStr, conn); + } +} diff --git a/athena-docdb/src/main/java/com/amazonaws/athena/connectors/docdb/DocDBFieldResolver.java b/athena-docdb/src/main/java/com/amazonaws/athena/connectors/docdb/DocDBFieldResolver.java new file mode 100644 index 0000000000..f805333ace --- /dev/null +++ b/athena-docdb/src/main/java/com/amazonaws/athena/connectors/docdb/DocDBFieldResolver.java @@ -0,0 +1,54 @@ +/*- + * #%L + * athena-mongodb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.docdb; + +import com.amazonaws.athena.connector.lambda.data.FieldResolver; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Field; +import org.bson.Document; + +import java.util.List; + +/** + * Used to resolve DocDB complex structures to Apache Arrow Types. + * + * @see com.amazonaws.athena.connector.lambda.data.FieldResolver + */ +public class DocDBFieldResolver + implements FieldResolver +{ + protected static final FieldResolver DEFAULT_FIELD_RESOLVER = new DocDBFieldResolver(); + + private DocDBFieldResolver() {} + + @Override + public Object getFieldValue(Field field, Object value) + { + Types.MinorType minorType = Types.getMinorTypeForArrowType(field.getType()); + if (minorType == Types.MinorType.LIST) { + return TypeUtils.coerce(field.getChildren().get(0), ((List) value).iterator()); + } + else if (value instanceof Document) { + Object rawVal = ((Document) value).get(field.getName()); + return TypeUtils.coerce(field, rawVal); + } + throw new RuntimeException("Expected LIST or Document type but found " + minorType); + } +} diff --git a/athena-docdb/src/main/java/com/amazonaws/athena/connectors/docdb/DocDBMetadataHandler.java b/athena-docdb/src/main/java/com/amazonaws/athena/connectors/docdb/DocDBMetadataHandler.java new file mode 100644 index 0000000000..5432061497 --- /dev/null +++ b/athena-docdb/src/main/java/com/amazonaws/athena/connectors/docdb/DocDBMetadataHandler.java @@ -0,0 +1,250 @@ +/*- + * #%L + * athena-mongodb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.docdb; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockWriter; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.spill.SpillLocation; +import com.amazonaws.athena.connector.lambda.handlers.GlueMetadataHandler; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesResponse; +import com.amazonaws.athena.connector.lambda.metadata.MetadataRequest; +import com.amazonaws.athena.connector.lambda.metadata.glue.GlueFieldLexer; +import com.amazonaws.athena.connector.lambda.security.EncryptionKeyFactory; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.glue.AWSGlue; +import com.amazonaws.services.glue.AWSGlueClientBuilder; +import com.amazonaws.services.glue.model.Table; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.mongodb.client.MongoClient; +import com.mongodb.client.MongoCursor; +import org.apache.arrow.util.VisibleForTesting; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.arrow.vector.types.pojo.Schema; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +/** + * Handles metadata requests for the Athena DocumentDB Connector. + *

+ * For more detail, please see the module's README.md, some notable characteristics of this class include: + *

+ * 1. Uses a Glue table property (docfb-metadata-flag) to indicate that the table (whose name matched the DocDB collection + * name) can indeed be used to supplement metadata from DocDB itself. + * 2. Attempts to resolve sensitive fields such as DocDB connection strings via SecretsManager so that you can substitute + * variables with values from by doing something like: + * mongodb://${docdb_instance_1_creds}@myhostname.com:123/?ssl=true&ssl_ca_certs=rds-combined-ca-bundle.pem&replicaSet=rs0 + */ +public class DocDBMetadataHandler + extends GlueMetadataHandler +{ + private static final Logger logger = LoggerFactory.getLogger(DocDBMetadataHandler.class); + + //Used to denote the 'type' of this connector for diagnostic purposes. + private static final String SOURCE_TYPE = "documentdb"; + //The Env variable name used to indicate that we want to disable the use of Glue DataCatalog for supplemental + //metadata and instead rely solely on the connector's schema inference capabilities. + private static final String GLUE_ENV_VAR = "disable_glue"; + //Field name used to store the connection string as a property on Split objects. + protected static final String DOCDB_CONN_STR = "connStr"; + //The Env variable name used to store the default DocDB connection string if no catalog specific + //env variable is set. + private static final String DEFAULT_DOCDB = "default_docdb"; + //The Glue table property that indicates that a table matching the name of an DocDB table + //is indeed enabled for use by this connector. + private static final String DOCDB_METADATA_FLAG = "docdb-metadata-flag"; + //Used to filter out Glue tables which lack a docdb metadata flag. + private static final TableFilter TABLE_FILTER = (Table table) -> table.getParameters().containsKey(DOCDB_METADATA_FLAG); + //The number of documents to scan when attempting to infer schema from an DocDB collection. + private static final int SCHEMA_INFERRENCE_NUM_DOCS = 10; + + private final AWSGlue glue; + private final DocDBConnectionFactory connectionFactory; + + public DocDBMetadataHandler() + { + super((System.getenv(GLUE_ENV_VAR) == null) ? AWSGlueClientBuilder.standard().build() : null, SOURCE_TYPE); + glue = getAwsGlue(); + connectionFactory = new DocDBConnectionFactory(); + } + + @VisibleForTesting + protected DocDBMetadataHandler(AWSGlue glue, + DocDBConnectionFactory connectionFactory, + EncryptionKeyFactory keyFactory, + AWSSecretsManager secretsManager, + AmazonAthena athena, + String spillBucket, + String spillPrefix) + { + super(glue, keyFactory, secretsManager, athena, SOURCE_TYPE, spillBucket, spillPrefix); + this.glue = glue; + this.connectionFactory = connectionFactory; + } + + private MongoClient getOrCreateConn(MetadataRequest request) + { + String endpoint = resolveSecrets(getConnStr(request)); + return connectionFactory.getOrCreateConn(endpoint); + } + + /** + * Retrieves the DocDB connection details from an env variable matching the catalog name, if no such + * env variable exists we fall back to the default env variable defined by DEFAULT_DOCDB. + */ + private String getConnStr(MetadataRequest request) + { + String conStr = System.getenv(request.getCatalogName()); + if (conStr == null) { + logger.info("getConnStr: No environment variable found for catalog {} , using default {}", + request.getCatalogName(), DEFAULT_DOCDB); + conStr = System.getenv(DEFAULT_DOCDB); + } + return conStr; + } + + /** + * List databases in your DocumentDB instance treating each as a 'schema' (aka database) + * + * @see GlueMetadataHandler + */ + @Override + public ListSchemasResponse doListSchemaNames(BlockAllocator blockAllocator, ListSchemasRequest request) + { + List schemas = new ArrayList<>(); + MongoClient client = getOrCreateConn(request); + try (MongoCursor itr = client.listDatabaseNames().iterator()) { + while (itr.hasNext()) { + schemas.add(itr.next()); + } + + return new ListSchemasResponse(request.getCatalogName(), schemas); + } + } + + /** + * List collections in the requested schema in your DocumentDB instance treating the requested schema as an DocumentDB + * database. + * + * @see GlueMetadataHandler + */ + @Override + public ListTablesResponse doListTables(BlockAllocator blockAllocator, ListTablesRequest request) + { + MongoClient client = getOrCreateConn(request); + List tables = new ArrayList<>(); + + try (MongoCursor itr = client.getDatabase(request.getSchemaName()).listCollectionNames().iterator()) { + while (itr.hasNext()) { + tables.add(new TableName(request.getSchemaName(), itr.next())); + } + + return new ListTablesResponse(request.getCatalogName(), tables); + } + } + + /** + * If Glue is enabled as a source of supplemental metadata we look up the requested Schema/Table in Glue and + * filters out any results that don't have the DOCDB_METADATA_FLAG set. If no matching results were found in Glue, + * then we resort to inferring the schema of the DocumentDB collection using SchemaUtils.inferSchema(...). If there + * is no such table in DocumentDB the operation will fail. + * + * @see GlueMetadataHandler + */ + @Override + public GetTableResponse doGetTable(BlockAllocator blockAllocator, GetTableRequest request) + throws Exception + { + logger.info("doGetTable: enter", request.getTableName()); + Schema schema = null; + try { + if (glue != null) { + schema = super.doGetTable(blockAllocator, request, TABLE_FILTER).getSchema(); + logger.info("doGetTable: Retrieved schema for table[{}] from AWS Glue.", request.getTableName()); + } + } + catch (RuntimeException ex) { + logger.warn("doGetTable: Unable to retrieve table[{}:{}] from AWS Glue.", + request.getTableName().getSchemaName(), + request.getTableName().getTableName(), + ex); + } + + if (schema == null) { + logger.info("doGetTable: Inferring schema for table[{}].", request.getTableName()); + MongoClient client = getOrCreateConn(request); + schema = SchemaUtils.inferSchema(client, request.getTableName(), SCHEMA_INFERRENCE_NUM_DOCS); + } + return new GetTableResponse(request.getCatalogName(), request.getTableName(), schema); + } + + /** + * Our table doesn't support complex layouts or partitioning so we simply make this method a NoOp. + * + * @see GlueMetadataHandler + */ + @Override + public void getPartitions(BlockWriter blockWriter, GetTableLayoutRequest request, QueryStatusChecker queryStatusChecker) + throws Exception + { + //NoOp as we do not support partitioning. + } + + /** + * Since our connector does not support parallel scans we generate a single Split and include the connection details + * as a property on the split so that the RecordHandler has easy access to it. + * + * @see GlueMetadataHandler + */ + @Override + public GetSplitsResponse doGetSplits(BlockAllocator blockAllocator, GetSplitsRequest request) + { + //Every split must have a unique location if we wish to spill to avoid failures + SpillLocation spillLocation = makeSpillLocation(request); + + //Since our connector does not support parallel reads we return a fixed split. + return new GetSplitsResponse(request.getCatalogName(), + Split.newBuilder(spillLocation, makeEncryptionKey()) + .add(DOCDB_CONN_STR, getConnStr(request)) + .build()); + } + + /** + * @see GlueMetadataHandler + */ + @Override + protected Field convertField(String name, String glueType) + { + return GlueFieldLexer.lex(name, glueType); + } +} diff --git a/athena-docdb/src/main/java/com/amazonaws/athena/connectors/docdb/DocDBRecordHandler.java b/athena-docdb/src/main/java/com/amazonaws/athena/connectors/docdb/DocDBRecordHandler.java new file mode 100644 index 0000000000..73ea87c9f1 --- /dev/null +++ b/athena-docdb/src/main/java/com/amazonaws/athena/connectors/docdb/DocDBRecordHandler.java @@ -0,0 +1,169 @@ +/*- + * #%L + * athena-mongodb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.docdb; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockSpiller; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import com.amazonaws.athena.connector.lambda.handlers.RecordHandler; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.athena.AmazonAthenaClientBuilder; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.amazonaws.services.secretsmanager.AWSSecretsManagerClientBuilder; +import com.mongodb.client.MongoClient; +import com.mongodb.client.MongoCollection; +import com.mongodb.client.MongoCursor; +import com.mongodb.client.MongoDatabase; +import org.apache.arrow.util.VisibleForTesting; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Field; +import org.bson.Document; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; + +import static com.amazonaws.athena.connectors.docdb.DocDBFieldResolver.DEFAULT_FIELD_RESOLVER; +import static com.amazonaws.athena.connectors.docdb.DocDBMetadataHandler.DOCDB_CONN_STR; + +/** + * Handles data read record requests for the Athena DocumentDB Connector. + *

+ * For more detail, please see the module's README.md, some notable characteristics of this class include: + *

+ * 1. Attempts to resolve sensitive configuration fields such as HBase connection string via SecretsManager so that you can + * substitute variables with values from by doing something like hostname:port:password=${my_secret} + */ +public class DocDBRecordHandler + extends RecordHandler +{ + private static final Logger logger = LoggerFactory.getLogger(DocDBRecordHandler.class); + + //Used to denote the 'type' of this connector for diagnostic purposes. + private static final String SOURCE_TYPE = "documentdb"; + //Controls the page size for fetching batches of documents from the MongoDB client. + private static final int MONGO_QUERY_BATCH_SIZE = 100; + + private final DocDBConnectionFactory connectionFactory; + + public DocDBRecordHandler() + { + this(AmazonS3ClientBuilder.defaultClient(), + AWSSecretsManagerClientBuilder.defaultClient(), + AmazonAthenaClientBuilder.defaultClient(), + new DocDBConnectionFactory()); + } + + @VisibleForTesting + protected DocDBRecordHandler(AmazonS3 amazonS3, AWSSecretsManager secretsManager, AmazonAthena athena, DocDBConnectionFactory connectionFactory) + { + super(amazonS3, secretsManager, athena, SOURCE_TYPE); + this.connectionFactory = connectionFactory; + } + + /** + * Gets the special DOCDB_CONN_STR property from the provided split and uses its contents to getOrCreate + * a MongoDB client connection. + * + * @param split The split to that we need to read and this the DocDB instance to connecto ro. + * @return A MongoClient connected to the request DB instance. + * @note This method attempts to resolve any SecretsManager secrets that are using in the connection string and denoted + * by ${secret_name}. + */ + private MongoClient getOrCreateConn(Split split) + { + String conStr = split.getProperty(DOCDB_CONN_STR); + if (conStr == null) { + throw new RuntimeException(DOCDB_CONN_STR + " Split property is null! Unable to create connection."); + } + String endpoint = resolveSecrets(conStr); + return connectionFactory.getOrCreateConn(endpoint); + } + + /** + * Scans DocumentDB using the scan settings set on the requested Split by DocDBeMetadataHandler. + * + * @see RecordHandler + */ + @Override + protected void readWithConstraint(BlockSpiller spiller, ReadRecordsRequest recordsRequest, QueryStatusChecker queryStatusChecker) + { + TableName tableName = recordsRequest.getTableName(); + Map constraintSummary = recordsRequest.getConstraints().getSummary(); + + MongoClient client = getOrCreateConn(recordsRequest.getSplit()); + MongoDatabase db = client.getDatabase(tableName.getSchemaName()); + MongoCollection table = db.getCollection(tableName.getTableName()); + + Document query = QueryUtils.makeQuery(recordsRequest.getSchema(), constraintSummary); + Document output = QueryUtils.makeProjection(recordsRequest.getSchema()); + + logger.info("readWithConstraint: query[{}] projection[{}]", query, output); + + final MongoCursor iterable = table + .find(query) + .projection(output) + .batchSize(MONGO_QUERY_BATCH_SIZE).iterator(); + + long numRows = 0; + AtomicLong numResultRows = new AtomicLong(0); + while (iterable.hasNext() && queryStatusChecker.isQueryRunning()) { + numRows++; + spiller.writeRows((Block block, int rowNum) -> { + Document doc = iterable.next(); + + boolean matched = true; + for (Field nextField : recordsRequest.getSchema().getFields()) { + Object value = TypeUtils.coerce(nextField, doc.get(nextField.getName())); + Types.MinorType fieldType = Types.getMinorTypeForArrowType(nextField.getType()); + try { + switch (fieldType) { + case LIST: + case STRUCT: + matched &= block.offerComplexValue(nextField.getName(), rowNum, DEFAULT_FIELD_RESOLVER, value); + break; + default: + matched &= block.offerValue(nextField.getName(), rowNum, value); + break; + } + if (!matched) { + return 0; + } + } + catch (Exception ex) { + throw new RuntimeException("Error while processing field " + nextField.getName(), ex); + } + } + + numResultRows.getAndIncrement(); + return 1; + }); + } + + logger.info("readWithConstraint: numRows[{}] numResultRows[{}]", numRows, numResultRows.get()); + } +} diff --git a/athena-docdb/src/main/java/com/amazonaws/athena/connectors/docdb/QueryUtils.java b/athena-docdb/src/main/java/com/amazonaws/athena/connectors/docdb/QueryUtils.java new file mode 100644 index 0000000000..fdf2b17191 --- /dev/null +++ b/athena-docdb/src/main/java/com/amazonaws/athena/connectors/docdb/QueryUtils.java @@ -0,0 +1,247 @@ +/*- + * #%L + * athena-mongodb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @note Portions of this file are attributable to: + * https://github.com/prestodb/presto/blob/master/presto-mongodb/src/main/java/com/facebook/presto/mongodb/MongoSession.java + */ +package com.amazonaws.athena.connectors.docdb; + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.domain.predicate.EquatableValueSet; +import com.amazonaws.athena.connector.lambda.domain.predicate.Range; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import org.apache.arrow.vector.complex.reader.FieldReader; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.arrow.vector.types.pojo.Schema; +import org.apache.arrow.vector.util.Text; +import org.bson.Document; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.base.Verify.verify; +import static java.util.stream.Collectors.toList; + +/** + * Collection of helper methods which build Documents for use in DocumentDB queries, including: + * 1. Projections + * 2. Predicates + * 3. Queries (a collection of predicates) + */ +public final class QueryUtils +{ + private static final String OR_OP = "$or"; + private static final String AND_OP = "$and"; + private static final String NOT_OP = "$not"; + private static final String NOR_OP = "$nor"; + + private static final String EQ_OP = "$eq"; + private static final String NOT_EQ_OP = "$ne"; + private static final String EXISTS_OP = "$exists"; + private static final String GTE_OP = "$gte"; + private static final String GT_OP = "$gt"; + private static final String LT_OP = "$lt"; + private static final String LTE_OP = "$lte"; + private static final String IN_OP = "$in"; + private static final String NOTIN_OP = "$nin"; + + private QueryUtils() + { + } + + /** + * Given a Schema create a projection document which can be used to request only specific Document fields + * from DocumentDB. + * + * @param schema The schema containing the requested projection. + * @return A Document matching the requested field projections. + */ + public static Document makeProjection(Schema schema) + { + Document output = new Document(); + for (Field field : schema.getFields()) { + output.append(field.getName(), 1); + } + return output; + } + + /** + * Given a set of Constraints and the projection Schema, create the Query Document that can be used to + * push predicates into DocumentDB. + * + * @param schema The schema containing the requested projection. + * @param constraintSummary The set of constraints to apply to the query. + * @return The Document to use as the query. + */ + public static Document makeQuery(Schema schema, Map constraintSummary) + { + Document query = new Document(); + for (Map.Entry entry : constraintSummary.entrySet()) { + Document doc = makePredicate(schema.findField(entry.getKey()), entry.getValue()); + if (doc != null) { + query.putAll(doc); + } + } + + return query; + } + + /** + * Converts a single field constraint into a Document for use in a DocumentDB query. + * + * @param field The field for the given ValueSet constraint. + * @param constraint The constraint to apply to the given field. + * @return A Document describing the constraint for pushing down into DocumentDB. + */ + public static Document makePredicate(Field field, ValueSet constraint) + { + String name = field.getName(); + + if (constraint.isNone()) { + return documentOf(name, isNullPredicate()); + } + + if (constraint.isAll()) { + return documentOf(name, isNotNullPredicate()); + } + + if (constraint.isNullAllowed()) { + //TODO: support nulls mixed with discrete value constraints + return null; + } + + if (constraint instanceof EquatableValueSet) { + Block block = ((EquatableValueSet) constraint).getValues(); + List singleValues = new ArrayList<>(); + + FieldReader fieldReader = block.getFieldReaders().get(0); + for (int i = 0; i < block.getRowCount(); i++) { + Document nextEqVal = new Document(); + Object value = fieldReader.readObject(); + nextEqVal.put(EQ_OP, convert(value)); + singleValues.add(singleValues); + } + + return orPredicate(singleValues.stream() + .map(next -> new Document(name, next)) + .collect(toList())); + } + + List singleValues = new ArrayList<>(); + List disjuncts = new ArrayList<>(); + for (Range range : constraint.getRanges().getOrderedRanges()) { + if (range.isSingleValue()) { + singleValues.add(convert(range.getSingleValue())); + } + else { + Document rangeConjuncts = new Document(); + if (!range.getLow().isLowerUnbounded()) { + switch (range.getLow().getBound()) { + case ABOVE: + rangeConjuncts.put(GT_OP, convert(range.getLow().getValue())); + break; + case EXACTLY: + rangeConjuncts.put(GTE_OP, convert(range.getLow().getValue())); + break; + case BELOW: + throw new IllegalArgumentException("Low Marker should never use BELOW bound: " + range); + default: + throw new AssertionError("Unhandled bound: " + range.getLow().getBound()); + } + } + if (!range.getHigh().isUpperUnbounded()) { + switch (range.getHigh().getBound()) { + case ABOVE: + throw new IllegalArgumentException("High Marker should never use ABOVE bound: " + range); + case EXACTLY: + rangeConjuncts.put(LTE_OP, convert(range.getHigh().getValue())); + break; + case BELOW: + rangeConjuncts.put(LT_OP, convert(range.getHigh().getValue())); + break; + default: + throw new AssertionError("Unhandled bound: " + range.getHigh().getBound()); + } + } + // If rangeConjuncts is null, then the range was ALL, which should already have been checked for + verify(!rangeConjuncts.isEmpty()); + disjuncts.add(rangeConjuncts); + } + } + + // Add back all of the possible single values either as an equality or an IN predicate + if (singleValues.size() == 1) { + disjuncts.add(documentOf(EQ_OP, singleValues.get(0))); + } + else if (singleValues.size() > 1) { + disjuncts.add(documentOf(IN_OP, singleValues)); + } + + return orPredicate(disjuncts.stream() + .map(disjunct -> new Document(name, disjunct)) + .collect(toList())); + } + + private static Document documentOf(String key, Object value) + { + return new Document(key, value); + } + + private static Document orPredicate(List values) + { + checkState(!values.isEmpty()); + if (values.size() == 1) { + return values.get(0); + } + return new Document(OR_OP, values); + } + + private static Document isNullPredicate() + { + return documentOf(EXISTS_OP, true).append(EQ_OP, null); + } + + private static Document isNotNullPredicate() + { + return documentOf(NOT_EQ_OP, null); + } + + private static Object convert(Object value) + { + if (value instanceof Text) { + return ((Text) value).toString(); + } + return value; + } +} diff --git a/athena-docdb/src/main/java/com/amazonaws/athena/connectors/docdb/SchemaUtils.java b/athena-docdb/src/main/java/com/amazonaws/athena/connectors/docdb/SchemaUtils.java new file mode 100644 index 0000000000..9a2117dc7f --- /dev/null +++ b/athena-docdb/src/main/java/com/amazonaws/athena/connectors/docdb/SchemaUtils.java @@ -0,0 +1,158 @@ +/*- + * #%L + * athena-mongodb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.docdb; + +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.mongodb.client.MongoClient; +import com.mongodb.client.MongoCursor; +import com.mongodb.client.MongoDatabase; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.arrow.vector.types.pojo.FieldType; +import org.apache.arrow.vector.types.pojo.Schema; +import org.bson.Document; +import org.bson.types.ObjectId; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Collection of helpful utilities that handle DocumentDB schema inference, type, and naming conversion. + */ +public class SchemaUtils +{ + private static final Logger logger = LoggerFactory.getLogger(SchemaUtils.class); + + private SchemaUtils() {} + + /** + * This method will produce an Apache Arrow Schema for the given TableName and DocumentDB connection + * by scanning up to the requested number of rows and using basic schema inference to determine + * data types. + * + * @param client The DocumentDB connection to use for the scan operation. + * @param table The DocumentDB TableName for which to produce an Apache Arrow Schema. + * @param numObjToSample The number of records to scan as part of producing the Schema. + * @return An Apache Arrow Schema representing the schema of the HBase table. + * @note The resulting schema is a union of the schema of every row that is scanned. Presently the code does not + * attempt to resolve conflicts if unique field has different types across documents. It is recommend that you + * use AWS Glue to define a schema for tables which may have such conflicts. In the future we may enhance this method + * to use a reasonable default (like String) and coerce heterogeneous fields to avoid query failure but forcing + * explicit handling by defining Schema in AWS Glue is likely a better approach. + */ + public static Schema inferSchema(MongoClient client, TableName table, int numObjToSample) + { + MongoDatabase db = client.getDatabase(table.getSchemaName()); + + try (MongoCursor docs = db.getCollection(table.getTableName()).find().batchSize(numObjToSample) + .maxScan(numObjToSample).limit(numObjToSample).iterator()) { + if (!docs.hasNext()) { + return SchemaBuilder.newBuilder().build(); + } + SchemaBuilder schemaBuilder = SchemaBuilder.newBuilder(); + + Set discoveredColumns = new HashSet<>(); + while (docs.hasNext()) { + Document doc = docs.next(); + for (String key : doc.keySet()) { + if (!discoveredColumns.contains(key)) { + schemaBuilder.addField(getArrowField(key, doc.get(key))); + discoveredColumns.add(key); + } + } + } + + return schemaBuilder.build(); + } + } + + /** + * Infers the type of a single DocumentDB document field. + * + * @param key The key of the field we are attempting to infer. + * @param value A value from the key whose type we are attempting to infer. + * @return The Apache Arrow field definition of the inferred key/value. + */ + public static Field getArrowField(String key, Object value) + { + if (value instanceof String) { + return new Field(key, FieldType.nullable(Types.MinorType.VARCHAR.getType()), null); + } + else if (value instanceof Integer) { + return new Field(key, FieldType.nullable(Types.MinorType.INT.getType()), null); + } + else if (value instanceof Long) { + return new Field(key, FieldType.nullable(Types.MinorType.BIGINT.getType()), null); + } + else if (value instanceof Boolean) { + return new Field(key, FieldType.nullable(Types.MinorType.BIT.getType()), null); + } + else if (value instanceof Float) { + return new Field(key, FieldType.nullable(Types.MinorType.FLOAT4.getType()), null); + } + else if (value instanceof Double) { + return new Field(key, FieldType.nullable(Types.MinorType.FLOAT8.getType()), null); + } + else if (value instanceof Date) { + return new Field(key, FieldType.nullable(Types.MinorType.DATEMILLI.getType()), null); + } + else if (value instanceof ObjectId) { + return new Field(key, FieldType.nullable(Types.MinorType.VARCHAR.getType()), null); + } + else if (value instanceof List) { + Field child; + if (((List) value).isEmpty()) { + try { + Object subVal = ((List) value).getClass() + .getTypeParameters()[0].getGenericDeclaration().newInstance(); + child = getArrowField("", subVal); + } + catch (IllegalAccessException | InstantiationException ex) { + throw new RuntimeException(ex); + } + } + else { + child = getArrowField("", ((List) value).get(0)); + } + return new Field(key, FieldType.nullable(Types.MinorType.LIST.getType()), + Collections.singletonList(child)); + } + else if (value instanceof Document) { + List children = new ArrayList<>(); + Document doc = (Document) value; + for (String childKey : doc.keySet()) { + Object childVal = doc.get(childKey); + Field child = getArrowField(childKey, childVal); + children.add(child); + } + return new Field(key, FieldType.nullable(Types.MinorType.STRUCT.getType()), children); + } + + String className = value.getClass() == null ? "null" : value.getClass().getName(); + throw new RuntimeException("Unknown type[" + className + "] for field[" + key + "]"); + } +} diff --git a/athena-docdb/src/main/java/com/amazonaws/athena/connectors/docdb/TypeUtils.java b/athena-docdb/src/main/java/com/amazonaws/athena/connectors/docdb/TypeUtils.java new file mode 100644 index 0000000000..0ae8391ab9 --- /dev/null +++ b/athena-docdb/src/main/java/com/amazonaws/athena/connectors/docdb/TypeUtils.java @@ -0,0 +1,92 @@ +/*- + * #%L + * athena-mongodb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.docdb; + +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.arrow.vector.types.pojo.Field; +import org.bson.types.ObjectId; + +/** + * Helper class with useful methods for type conversion and coercion. + */ +public class TypeUtils +{ + private TypeUtils() {} + + /** + * Allows for coercing types in the event that schema has evolved or there were other data issues. + * + * @param field The field that we are coercing the value into. + * @param origVal The value to coerce + * @return The coerced value. + * @note This method does only basic coercion today but will likely support more advanced + * coercions in the future as a way of dealing with schema evolution. + */ + public static Object coerce(Field field, Object origVal) + { + if (origVal == null) { + return origVal; + } + + if (origVal instanceof ObjectId) { + return origVal.toString(); + } + + ArrowType arrowType = field.getType(); + Types.MinorType minorType = Types.getMinorTypeForArrowType(arrowType); + + switch (minorType) { + case VARCHAR: + if (origVal instanceof String) { + return origVal; + } + else { + return String.valueOf(origVal); + } + case FLOAT8: + if (origVal instanceof Integer) { + return Double.valueOf((int) origVal); + } + else if (origVal instanceof Float) { + return Double.valueOf((float) origVal); + } + return origVal; + case FLOAT4: + if (origVal instanceof Integer) { + return Float.valueOf((int) origVal); + } + else if (origVal instanceof Double) { + return ((Double) origVal).floatValue(); + } + return origVal; + case INT: + if (origVal instanceof Float) { + return ((Float) origVal).intValue(); + } + else if (origVal instanceof Double) { + return ((Double) origVal).intValue(); + } + return origVal; + default: + return origVal; + } + } +} diff --git a/athena-docdb/src/test/java/com/amazonaws/athena/connectors/docdb/DocDBConnectionFactoryTest.java b/athena-docdb/src/test/java/com/amazonaws/athena/connectors/docdb/DocDBConnectionFactoryTest.java new file mode 100644 index 0000000000..9ef845a11c --- /dev/null +++ b/athena-docdb/src/test/java/com/amazonaws/athena/connectors/docdb/DocDBConnectionFactoryTest.java @@ -0,0 +1,58 @@ +/*- + * #%L + * athena-mongodb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.docdb; + +import com.mongodb.client.MongoClient; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class DocDBConnectionFactoryTest +{ + private DocDBConnectionFactory connectionFactory; + + @Before + public void setUp() + throws Exception + { + connectionFactory = new DocDBConnectionFactory(); + } + + @Test + public void clientCacheHitTest() + throws IOException + { + MongoClient mockConn = mock(MongoClient.class); + when(mockConn.listDatabaseNames()).thenReturn(null); + + connectionFactory.addConnection("conStr", mockConn); + MongoClient conn = connectionFactory.getOrCreateConn("conStr"); + + assertEquals(mockConn, conn); + verify(mockConn, times(1)).listDatabaseNames(); + } +} diff --git a/athena-docdb/src/test/java/com/amazonaws/athena/connectors/docdb/DocDBMetadataHandlerTest.java b/athena-docdb/src/test/java/com/amazonaws/athena/connectors/docdb/DocDBMetadataHandlerTest.java new file mode 100644 index 0000000000..f572ff6573 --- /dev/null +++ b/athena-docdb/src/test/java/com/amazonaws/athena/connectors/docdb/DocDBMetadataHandlerTest.java @@ -0,0 +1,311 @@ +/*- + * #%L + * athena-mongodb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.docdb; + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.amazonaws.athena.connector.lambda.data.BlockUtils; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesResponse; +import com.amazonaws.athena.connector.lambda.metadata.MetadataRequestType; +import com.amazonaws.athena.connector.lambda.metadata.MetadataResponse; +import com.amazonaws.athena.connector.lambda.security.FederatedIdentity; +import com.amazonaws.athena.connector.lambda.security.LocalKeyFactory; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.glue.AWSGlue; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.mongodb.client.FindIterable; +import com.mongodb.client.MongoClient; +import com.mongodb.client.MongoCollection; +import com.mongodb.client.MongoDatabase; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.arrow.vector.types.pojo.Schema; +import org.bson.Document; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class DocDBMetadataHandlerTest +{ + private static final Logger logger = LoggerFactory.getLogger(DocDBMetadataHandlerTest.class); + + private FederatedIdentity identity = new FederatedIdentity("id", "principal", "account"); + private String catalog = "default"; + private DocDBMetadataHandler handler; + private BlockAllocator allocator; + + @Mock + private DocDBConnectionFactory connectionFactory; + + @Mock + private MongoClient mockClient; + + @Mock + private AWSGlue awsGlue; + + @Mock + private AWSSecretsManager secretsManager; + + @Mock + private AmazonAthena mockAthena; + + @Before + public void setUp() + throws Exception + { + when(connectionFactory.getOrCreateConn(anyString())).thenReturn(mockClient); + + handler = new DocDBMetadataHandler(awsGlue, connectionFactory, new LocalKeyFactory(), secretsManager, mockAthena, "spillBucket", "spillPrefix"); + allocator = new BlockAllocatorImpl(); + } + + @After + public void tearDown() + throws Exception + { + allocator.close(); + } + + @Test + public void doListSchemaNames() + { + logger.info("doListSchemaNames: enter"); + + List schemaNames = new ArrayList<>(); + schemaNames.add("schema1"); + schemaNames.add("schema2"); + schemaNames.add("schema3"); + + when(mockClient.listDatabaseNames()).thenReturn(StubbingCursor.iterate(schemaNames)); + + ListSchemasRequest req = new ListSchemasRequest(identity, "queryId", "default"); + ListSchemasResponse res = handler.doListSchemaNames(allocator, req); + + logger.info("doListSchemas - {}", res.getSchemas()); + assertEquals(schemaNames, new ArrayList<>(res.getSchemas())); + + logger.info("doListSchemaNames: exit"); + } + + @Test + public void doListTables() + { + logger.info("doListTables - enter"); + + String schema = "schema1"; + + List tableNames = new ArrayList<>(); + tableNames.add("table1"); + tableNames.add("table2"); + tableNames.add("table3"); + + MongoDatabase mockDatabase = mock(MongoDatabase.class); + when(mockClient.getDatabase(eq(schema))).thenReturn(mockDatabase); + when(mockDatabase.listCollectionNames()).thenReturn(StubbingCursor.iterate(tableNames)); + + ListTablesRequest req = new ListTablesRequest(identity, "queryId", "default", schema); + ListTablesResponse res = handler.doListTables(allocator, req); + logger.info("doListTables - {}", res.getTables()); + + for (TableName next : res.getTables()) { + assertEquals(schema, next.getSchemaName()); + assertTrue(tableNames.contains(next.getTableName())); + } + assertEquals(tableNames.size(), res.getTables().size()); + + logger.info("doListTables - exit"); + } + + /** + * TODO: Add more types. + */ + @Test + public void doGetTable() + throws Exception + { + logger.info("doGetTable - enter"); + + String schema = "schema1"; + String table = "table1"; + + List documents = new ArrayList<>(); + + Document doc1 = new Document(); + documents.add(doc1); + doc1.put("stringCol", "stringVal"); + doc1.put("intCol", 1); + doc1.put("doubleCol", 2.2D); + doc1.put("longCol", 100L); + + Document doc2 = new Document(); + documents.add(doc2); + doc2.put("stringCol2", "stringVal"); + doc2.put("intCol2", 1); + doc2.put("doubleCol2", 2.2D); + doc2.put("longCol2", 100L); + + Document doc3 = new Document(); + documents.add(doc3); + doc3.put("stringCol", "stringVal"); + doc3.put("intCol2", 1); + doc3.put("doubleCol", 2.2D); + doc3.put("longCol2", 100L); + + MongoDatabase mockDatabase = mock(MongoDatabase.class); + MongoCollection mockCollection = mock(MongoCollection.class); + FindIterable mockIterable = mock(FindIterable.class); + when(mockClient.getDatabase(eq(schema))).thenReturn(mockDatabase); + when(mockDatabase.getCollection(eq(table))).thenReturn(mockCollection); + when(mockCollection.find()).thenReturn(mockIterable); + when(mockIterable.limit(anyInt())).thenReturn(mockIterable); + when(mockIterable.maxScan(anyInt())).thenReturn(mockIterable); + when(mockIterable.batchSize(anyInt())).thenReturn(mockIterable); + when(mockIterable.iterator()).thenReturn(new StubbingCursor(documents.iterator())); + + GetTableRequest req = new GetTableRequest(identity, "queryId", catalog, new TableName(schema, table)); + GetTableResponse res = handler.doGetTable(allocator, req); + logger.info("doGetTable - {}", res); + + assertEquals(8, res.getSchema().getFields().size()); + + Field stringCol = res.getSchema().findField("stringCol"); + assertEquals(Types.MinorType.VARCHAR, Types.getMinorTypeForArrowType(stringCol.getType())); + + Field stringCol2 = res.getSchema().findField("stringCol2"); + assertEquals(Types.MinorType.VARCHAR, Types.getMinorTypeForArrowType(stringCol2.getType())); + + Field intCol = res.getSchema().findField("intCol"); + assertEquals(Types.MinorType.INT, Types.getMinorTypeForArrowType(intCol.getType())); + + Field intCol2 = res.getSchema().findField("intCol2"); + assertEquals(Types.MinorType.INT, Types.getMinorTypeForArrowType(intCol2.getType())); + + Field doubleCol = res.getSchema().findField("doubleCol"); + assertEquals(Types.MinorType.FLOAT8, Types.getMinorTypeForArrowType(doubleCol.getType())); + + Field doubleCol2 = res.getSchema().findField("doubleCol2"); + assertEquals(Types.MinorType.FLOAT8, Types.getMinorTypeForArrowType(doubleCol2.getType())); + + Field longCol = res.getSchema().findField("longCol"); + assertEquals(Types.MinorType.BIGINT, Types.getMinorTypeForArrowType(longCol.getType())); + + Field longCol2 = res.getSchema().findField("longCol2"); + assertEquals(Types.MinorType.BIGINT, Types.getMinorTypeForArrowType(longCol2.getType())); + + logger.info("doGetTable - exit"); + } + + @Test + public void doGetTableLayout() + throws Exception + { + logger.info("doGetTableLayout - enter"); + + Schema schema = SchemaBuilder.newBuilder().build(); + GetTableLayoutRequest req = new GetTableLayoutRequest(identity, + "queryId", + "default", + new TableName("schema1", "table1"), + new Constraints(new HashMap<>()), + schema, + Collections.EMPTY_SET); + + GetTableLayoutResponse res = handler.doGetTableLayout(allocator, req); + + logger.info("doGetTableLayout - {}", res); + Block partitions = res.getPartitions(); + for (int row = 0; row < partitions.getRowCount() && row < 10; row++) { + logger.info("doGetTableLayout:{} {}", row, BlockUtils.rowToString(partitions, row)); + } + + assertTrue(partitions.getRowCount() > 0); + + logger.info("doGetTableLayout: partitions[{}]", partitions.getRowCount()); + } + + @Test + public void doGetSplits() + { + logger.info("doGetSplits: enter"); + + List partitionCols = new ArrayList<>(); + + Block partitions = BlockUtils.newBlock(allocator, "partitionId", Types.MinorType.INT.getType(), 0); + + String continuationToken = null; + GetSplitsRequest originalReq = new GetSplitsRequest(identity, + "queryId", + "catalog_name", + new TableName("schema", "table_name"), + partitions, + partitionCols, + new Constraints(new HashMap<>()), + null); + + GetSplitsRequest req = new GetSplitsRequest(originalReq, continuationToken); + + logger.info("doGetSplits: req[{}]", req); + + MetadataResponse rawResponse = handler.doGetSplits(allocator, req); + assertEquals(MetadataRequestType.GET_SPLITS, rawResponse.getRequestType()); + + GetSplitsResponse response = (GetSplitsResponse) rawResponse; + continuationToken = response.getContinuationToken(); + + logger.info("doGetSplits: continuationToken[{}] - numSplits[{}]", + new Object[] {continuationToken, response.getSplits().size()}); + + assertTrue("Continuation criteria violated", response.getSplits().size() == 1); + assertTrue("Continuation criteria violated", response.getContinuationToken() == null); + + logger.info("doGetSplits: exit"); + } +} diff --git a/athena-docdb/src/test/java/com/amazonaws/athena/connectors/docdb/DocDBRecordHandlerTest.java b/athena-docdb/src/test/java/com/amazonaws/athena/connectors/docdb/DocDBRecordHandlerTest.java new file mode 100644 index 0000000000..6e5bd9d55b --- /dev/null +++ b/athena-docdb/src/test/java/com/amazonaws/athena/connectors/docdb/DocDBRecordHandlerTest.java @@ -0,0 +1,373 @@ +/*- + * #%L + * athena-mongodb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.docdb; + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.amazonaws.athena.connector.lambda.data.BlockUtils; +import com.amazonaws.athena.connector.lambda.data.S3BlockSpillReader; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.domain.predicate.Range; +import com.amazonaws.athena.connector.lambda.domain.predicate.SortedRangeSet; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import com.amazonaws.athena.connector.lambda.domain.spill.S3SpillLocation; +import com.amazonaws.athena.connector.lambda.domain.spill.SpillLocation; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsResponse; +import com.amazonaws.athena.connector.lambda.records.RecordResponse; +import com.amazonaws.athena.connector.lambda.records.RemoteReadRecordsResponse; +import com.amazonaws.athena.connector.lambda.security.EncryptionKeyFactory; +import com.amazonaws.athena.connector.lambda.security.FederatedIdentity; +import com.amazonaws.athena.connector.lambda.security.LocalKeyFactory; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.PutObjectResult; +import com.amazonaws.services.s3.model.S3Object; +import com.amazonaws.services.s3.model.S3ObjectInputStream; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.google.common.collect.ImmutableList; +import com.google.common.io.ByteStreams; +import com.mongodb.client.FindIterable; +import com.mongodb.client.MongoClient; +import com.mongodb.client.MongoCollection; +import com.mongodb.client.MongoDatabase; +import org.apache.arrow.vector.types.FloatingPointPrecision; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.arrow.vector.types.pojo.Schema; +import org.bson.Document; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.stubbing.Answer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import static com.amazonaws.athena.connectors.docdb.DocDBMetadataHandler.DOCDB_CONN_STR; +import static org.junit.Assert.*; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class DocDBRecordHandlerTest +{ + private static final Logger logger = LoggerFactory.getLogger(DocDBRecordHandlerTest.class); + + private FederatedIdentity identity = new FederatedIdentity("id", "principal", "account"); + private String catalog = "default"; + private String conStr = "connectionString"; + private DocDBRecordHandler handler; + private BlockAllocator allocator; + private List mockS3Storage = new ArrayList<>(); + private AmazonS3 amazonS3; + private S3BlockSpillReader spillReader; + private Schema schemaForRead; + private EncryptionKeyFactory keyFactory = new LocalKeyFactory(); + + @Mock + private DocDBConnectionFactory connectionFactory; + + @Mock + private MongoClient mockClient; + + @Mock + private AWSSecretsManager mockSecretsManager; + + @Mock + private AmazonAthena mockAthena; + + @Before + public void setUp() + { + logger.info("setUpBefore - enter"); + + schemaForRead = SchemaBuilder.newBuilder() + .addField("col1", new ArrowType.Int(32, true)) + .addField("col2", new ArrowType.Utf8()) + .addField("col3", new ArrowType.FloatingPoint(FloatingPointPrecision.DOUBLE)) + .addField("int", Types.MinorType.INT.getType()) + .addField("tinyint", Types.MinorType.TINYINT.getType()) + .addField("smallint", Types.MinorType.SMALLINT.getType()) + .addField("bigint", Types.MinorType.BIGINT.getType()) + .addField("uint1", Types.MinorType.UINT1.getType()) + .addField("uint2", Types.MinorType.UINT2.getType()) + .addField("uint4", Types.MinorType.UINT4.getType()) + .addField("uint8", Types.MinorType.UINT8.getType()) + .addField("float4", Types.MinorType.FLOAT4.getType()) + .addField("float8", Types.MinorType.FLOAT8.getType()) + .addField("bit", Types.MinorType.BIT.getType()) + .addField("varchar", Types.MinorType.VARCHAR.getType()) + .addField("varbinary", Types.MinorType.VARBINARY.getType()) + .addField("decimal", new ArrowType.Decimal(10, 2)) + .addField("decimalLong", new ArrowType.Decimal(36, 2)) + .addStructField("struct") + .addChildField("struct", "struct_string", Types.MinorType.VARCHAR.getType()) + .addChildField("struct", "struct_int", Types.MinorType.INT.getType()) + .addListField("list", Types.MinorType.VARCHAR.getType()) + .build(); + + when(connectionFactory.getOrCreateConn(anyString())).thenReturn(mockClient); + + allocator = new BlockAllocatorImpl(); + + amazonS3 = mock(AmazonS3.class); + + when(amazonS3.putObject(anyObject(), anyObject(), anyObject(), anyObject())) + .thenAnswer(new Answer() + { + @Override + public Object answer(InvocationOnMock invocationOnMock) + throws Throwable + { + InputStream inputStream = (InputStream) invocationOnMock.getArguments()[2]; + DocDBRecordHandlerTest.ByteHolder byteHolder = new ByteHolder(); + byteHolder.setBytes(ByteStreams.toByteArray(inputStream)); + mockS3Storage.add(byteHolder); + return mock(PutObjectResult.class); + } + }); + + when(amazonS3.getObject(anyString(), anyString())) + .thenAnswer(new Answer() + { + @Override + public Object answer(InvocationOnMock invocationOnMock) + throws Throwable + { + S3Object mockObject = mock(S3Object.class); + ByteHolder byteHolder = mockS3Storage.get(0); + mockS3Storage.remove(0); + when(mockObject.getObjectContent()).thenReturn( + new S3ObjectInputStream( + new ByteArrayInputStream(byteHolder.getBytes()), null)); + return mockObject; + } + }); + + handler = new DocDBRecordHandler(amazonS3, mockSecretsManager, mockAthena, connectionFactory); + spillReader = new S3BlockSpillReader(amazonS3, allocator); + + logger.info("setUpBefore - exit"); + } + + @After + public void after() + { + allocator.close(); + } + + private Document makeDocument(Schema schema, int seed) + { + Document doc = new Document(); + doc.put("stringCol", "stringVal"); + doc.put("intCol", 1); + doc.put("col3", 22.0D); + return doc; + } + + @Test + public void doReadRecordsNoSpill() + throws Exception + { + logger.info("doReadRecordsNoSpill: enter"); + + String schema = "schema1"; + String table = "table1"; + + List documents = new ArrayList<>(); + + int docNum = 11; + Document doc1 = DocumentGenerator.makeRandomRow(schemaForRead.getFields(), docNum++); + documents.add(doc1); + doc1.put("col3", 22.0D); + + Document doc2 = DocumentGenerator.makeRandomRow(schemaForRead.getFields(), docNum++); + documents.add(doc2); + doc2.put("col3", 22.0D); + + Document doc3 = DocumentGenerator.makeRandomRow(schemaForRead.getFields(), docNum++); + documents.add(doc3); + doc3.put("col3", 21.0D); + + MongoDatabase mockDatabase = mock(MongoDatabase.class); + MongoCollection mockCollection = mock(MongoCollection.class); + FindIterable mockIterable = mock(FindIterable.class); + when(mockClient.getDatabase(eq(schema))).thenReturn(mockDatabase); + when(mockDatabase.getCollection(eq(table))).thenReturn(mockCollection); + when(mockCollection.find(any(Document.class))).thenAnswer((InvocationOnMock invocationOnMock) -> { + logger.info("doReadRecordsNoSpill: query[{}]", invocationOnMock.getArguments()[0]); + return mockIterable; + }); + when(mockIterable.projection(any(Document.class))).thenAnswer((InvocationOnMock invocationOnMock) -> { + logger.info("doReadRecordsNoSpill: projection[{}]", invocationOnMock.getArguments()[0]); + return mockIterable; + }); + when(mockIterable.batchSize(anyInt())).thenReturn(mockIterable); + when(mockIterable.iterator()).thenReturn(new StubbingCursor(documents.iterator())); + + Map constraintsMap = new HashMap<>(); + constraintsMap.put("col3", SortedRangeSet.copyOf(Types.MinorType.FLOAT8.getType(), + ImmutableList.of(Range.equal(allocator, Types.MinorType.FLOAT8.getType(), 22.0D)), false)); + + S3SpillLocation splitLoc = S3SpillLocation.newBuilder() + .withBucket(UUID.randomUUID().toString()) + .withSplitId(UUID.randomUUID().toString()) + .withQueryId(UUID.randomUUID().toString()) + .withIsDirectory(true) + .build(); + + ReadRecordsRequest request = new ReadRecordsRequest(identity, + catalog, + "queryId-" + System.currentTimeMillis(), + new TableName(schema, table), + schemaForRead, + Split.newBuilder(splitLoc, keyFactory.create()).add(DOCDB_CONN_STR, conStr).build(), + new Constraints(constraintsMap), + 100_000_000_000L, //100GB don't expect this to spill + 100_000_000_000L + ); + + RecordResponse rawResponse = handler.doReadRecords(allocator, request); + + assertTrue(rawResponse instanceof ReadRecordsResponse); + + ReadRecordsResponse response = (ReadRecordsResponse) rawResponse; + logger.info("doReadRecordsNoSpill: rows[{}]", response.getRecordCount()); + + assertTrue(response.getRecords().getRowCount() == 2); + logger.info("doReadRecordsNoSpill: {}", BlockUtils.rowToString(response.getRecords(), 0)); + + logger.info("doReadRecordsNoSpill: exit"); + } + + @Test + public void doReadRecordsSpill() + throws Exception + { + logger.info("doReadRecordsSpill: enter"); + + String schema = "schema1"; + String table = "table1"; + + List documents = new ArrayList<>(); + + for (int docNum = 0; docNum < 20_000; docNum++) { + documents.add(DocumentGenerator.makeRandomRow(schemaForRead.getFields(), docNum)); + } + + MongoDatabase mockDatabase = mock(MongoDatabase.class); + MongoCollection mockCollection = mock(MongoCollection.class); + FindIterable mockIterable = mock(FindIterable.class); + when(mockClient.getDatabase(eq(schema))).thenReturn(mockDatabase); + when(mockDatabase.getCollection(eq(table))).thenReturn(mockCollection); + when(mockCollection.find(any(Document.class))).thenAnswer((InvocationOnMock invocationOnMock) -> { + logger.info("doReadRecordsNoSpill: query[{}]", invocationOnMock.getArguments()[0]); + return mockIterable; + }); + when(mockIterable.projection(any(Document.class))).thenAnswer((InvocationOnMock invocationOnMock) -> { + logger.info("doReadRecordsNoSpill: projection[{}]", invocationOnMock.getArguments()[0]); + return mockIterable; + }); + when(mockIterable.batchSize(anyInt())).thenReturn(mockIterable); + when(mockIterable.iterator()).thenReturn(new StubbingCursor(documents.iterator())); + + Map constraintsMap = new HashMap<>(); + constraintsMap.put("col3", SortedRangeSet.copyOf(Types.MinorType.FLOAT8.getType(), + ImmutableList.of(Range.greaterThan(allocator, Types.MinorType.FLOAT8.getType(), -10000D)), false)); + + S3SpillLocation splitLoc = S3SpillLocation.newBuilder() + .withBucket(UUID.randomUUID().toString()) + .withSplitId(UUID.randomUUID().toString()) + .withQueryId(UUID.randomUUID().toString()) + .withIsDirectory(true) + .build(); + + ReadRecordsRequest request = new ReadRecordsRequest(identity, + catalog, + "queryId-" + System.currentTimeMillis(), + new TableName(schema, table), + schemaForRead, + Split.newBuilder(splitLoc, keyFactory.create()).add(DOCDB_CONN_STR, conStr).build(), + new Constraints(constraintsMap), + 1_500_000L, //~1.5MB so we should see some spill + 0L + ); + RecordResponse rawResponse = handler.doReadRecords(allocator, request); + + assertTrue(rawResponse instanceof RemoteReadRecordsResponse); + + try (RemoteReadRecordsResponse response = (RemoteReadRecordsResponse) rawResponse) { + logger.info("doReadRecordsSpill: remoteBlocks[{}]", response.getRemoteBlocks().size()); + + assertTrue(response.getNumberBlocks() > 1); + + int blockNum = 0; + for (SpillLocation next : response.getRemoteBlocks()) { + S3SpillLocation spillLocation = (S3SpillLocation) next; + try (Block block = spillReader.read(spillLocation, response.getEncryptionKey(), response.getSchema())) { + + logger.info("doReadRecordsSpill: blockNum[{}] and recordCount[{}]", blockNum++, block.getRowCount()); + // assertTrue(++blockNum < response.getRemoteBlocks().size() && block.getRowCount() > 10_000); + + logger.info("doReadRecordsSpill: {}", BlockUtils.rowToString(block, 0)); + assertNotNull(BlockUtils.rowToString(block, 0)); + } + } + } + + logger.info("doReadRecordsSpill: exit"); + } + + private class ByteHolder + { + private byte[] bytes; + + public void setBytes(byte[] bytes) + { + this.bytes = bytes; + } + + public byte[] getBytes() + { + return bytes; + } + } +} diff --git a/athena-docdb/src/test/java/com/amazonaws/athena/connectors/docdb/DocumentGenerator.java b/athena-docdb/src/test/java/com/amazonaws/athena/connectors/docdb/DocumentGenerator.java new file mode 100644 index 0000000000..4ad482eac7 --- /dev/null +++ b/athena-docdb/src/test/java/com/amazonaws/athena/connectors/docdb/DocumentGenerator.java @@ -0,0 +1,115 @@ +/*- + * #%L + * athena-mongodb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.docdb; + +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Field; +import org.bson.Document; + +import java.util.ArrayList; +import java.util.List; + +public class DocumentGenerator +{ + private DocumentGenerator() {} + + /** + * This should be replaced with something that actually reads useful data. + */ + public static Document makeRandomRow(List fields, int seed) + { + Document result = new Document(); + + for (Field next : fields) { + boolean negative = seed % 2 == 1; + Types.MinorType minorType = Types.getMinorTypeForArrowType(next.getType()); + switch (minorType) { + case INT: + int iVal = seed * (negative ? -1 : 1); + result.put(next.getName(), iVal); + break; + case TINYINT: + case SMALLINT: + int stVal = (seed % 4) * (negative ? -1 : 1); + result.put(next.getName(), stVal); + break; + case UINT1: + case UINT2: + case UINT4: + case UINT8: + int uiVal = seed % 4; + result.put(next.getName(), uiVal); + break; + case FLOAT4: + float fVal = seed * 1.1f * (negative ? -1 : 1); + result.put(next.getName(), fVal); + break; + case FLOAT8: + case DECIMAL: + double d8Val = seed * 1.1D * (negative ? -1 : 1); + result.put(next.getName(), d8Val); + break; + case BIT: + boolean bVal = seed % 2 == 0; + result.put(next.getName(), bVal); + break; + case BIGINT: + long lVal = seed * 1L * (negative ? -1 : 1); + result.put(next.getName(), lVal); + break; + case VARCHAR: + String vVal = "VarChar" + seed; + result.put(next.getName(), vVal); + break; + case VARBINARY: + byte[] binaryVal = ("VarChar" + seed).getBytes(); + result.put(next.getName(), binaryVal); + break; + case STRUCT: + result.put(next.getName(), makeRandomRow(next.getChildren(), seed)); + break; + case LIST: + //TODO: pretty dirty way of generating lists should refactor this to support better generation + Types.MinorType listType = Types.getMinorTypeForArrowType(next.getChildren().get(0).getType()); + switch (listType) { + case VARCHAR: + List listVarChar = new ArrayList<>(); + listVarChar.add("VarChar" + seed); + listVarChar.add("VarChar" + seed + 1); + result.put(next.getName(), listVarChar); + break; + case INT: + List listIVal = new ArrayList<>(); + listIVal.add(seed * (negative ? -1 : 1)); + listIVal.add(seed * (negative ? -1 : 1) + 1); + result.put(next.getName(), listIVal); + break; + default: + throw new RuntimeException(minorType + " is not supported in list"); + } + break; + default: + throw new RuntimeException(minorType + " is not supported"); + } + } + + return result; + } +} diff --git a/athena-docdb/src/test/java/com/amazonaws/athena/connectors/docdb/StubbingCursor.java b/athena-docdb/src/test/java/com/amazonaws/athena/connectors/docdb/StubbingCursor.java new file mode 100644 index 0000000000..f95668827c --- /dev/null +++ b/athena-docdb/src/test/java/com/amazonaws/athena/connectors/docdb/StubbingCursor.java @@ -0,0 +1,132 @@ +/*- + * #%L + * athena-mongodb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.docdb; + +import com.mongodb.Block; +import com.mongodb.Function; +import com.mongodb.ServerAddress; +import com.mongodb.ServerCursor; +import com.mongodb.client.MongoCursor; +import com.mongodb.client.MongoIterable; + +import java.util.Collection; +import java.util.Iterator; +import java.util.function.Consumer; + +public class StubbingCursor + implements MongoCursor +{ + private Iterator values; + + public StubbingCursor(Iterator result) + { + this.values = result; + } + + @Override + public void remove() + { + throw new UnsupportedOperationException(); + } + + @Override + public void forEachRemaining(Consumer action) + { + + } + + @Override + public void close() + { + + } + + @Override + public boolean hasNext() + { + return values.hasNext(); + } + + @Override + public T next() + { + return values.next(); + } + + @Override + public T tryNext() + { + throw new UnsupportedOperationException(); + } + + @Override + public ServerCursor getServerCursor() + { + throw new UnsupportedOperationException(); + } + + @Override + public ServerAddress getServerAddress() + { + throw new UnsupportedOperationException(); + } + + public static MongoIterable iterate(Collection result) + { + return new MongoIterable() + { + @Override + public MongoCursor iterator() + { + return new StubbingCursor<>(result.iterator()); + } + + @Override + public X first() + { + throw new UnsupportedOperationException(); + } + + @Override + public MongoIterable map(Function function) + { + throw new UnsupportedOperationException(); + } + + @Override + public void forEach(Block block) + { + throw new UnsupportedOperationException(); + } + + @Override + public > A into(A objects) + { + throw new UnsupportedOperationException(); + } + + @Override + public MongoIterable batchSize(int i) + { + return this; + } + }; + } +} diff --git a/athena-dynamodb/LICENSE.txt b/athena-dynamodb/LICENSE.txt new file mode 100644 index 0000000000..418de4c108 --- /dev/null +++ b/athena-dynamodb/LICENSE.txt @@ -0,0 +1,174 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. \ No newline at end of file diff --git a/athena-dynamodb/README.md b/athena-dynamodb/README.md new file mode 100644 index 0000000000..1bf551e9a5 --- /dev/null +++ b/athena-dynamodb/README.md @@ -0,0 +1,60 @@ +# Amazon Athena DynamoDB Connector + +This connector enables Amazon Athena to communicate with DynamoDB, making your tables accessible via SQL. + +## Usage + +### Parameters + +The Athena DynamoDB Connector exposes several configuration options via Lambda environment variables. More detail on the available parameters can be found below. + +1. **spill_bucket** - When the data returned by your Lambda function exceeds Lambda’s limits, this is the bucket that the data will be written to for Athena to read the excess from. (e.g. my_bucket) +2. **spill_prefix** - (Optional) Defaults to sub-folder in your bucket called 'athena-federation-spill'. Used in conjunction with spill_bucket, this is the path within the above bucket that large +responses are spilled to. You should configure an S3 lifecycle on this location to delete old spills after X days/Hours. +3. **kms_key_id** - (Optional) By default any data that is spilled to S3 is encrypted using AES-GCM and a randomly generated key. Setting a KMS Key ID allows your Lambda function to use KMS for key +generation for a stronger source of encryption keys. (e.g. a7e63k4b-8loc-40db-a2a1-4d0en2cd8331) +4. **disable_spill_encryption** - (Optional) Defaults to False so that any data that is spilled to S3 is encrypted using AES-GMC either with a randomly generated key or using KMS to generate keys. +Setting this to false will disable spill encryption. You may wish to disable this for improved performance, especially if your spill location in S3 uses S3 Server Side Encryption. (e.g. True or False) +5. **disable_glue** - (Optional) If set to false, the connector will no longer attempt to retrieve supplemental metadata from Glue. +6. **glue_catalog** - (Optional) Can be used to target a cross-account Glue catalog. By default the connector will attempt to get metadata from its own Glue account. + +### Setting Up Databases & Tables in Glue + +To enable a Glue Table for use with DynamoDB, you simply need to have a table that matches any DynamoDB Table that you'd like to supply supplemental metadata for (instead of relying on the DynamoDB +Connector's limited ability to infer schema). You can enable a Glue table to be used for supplemental metadata by setting one of the below table properties from the Glue Console when editing the Table in +question. These properties are automatically set if you use Glue's DynamoDB Crawler. The only other thing you need to do is ensure you use the appropriate data types when defining manually or validate +the columns and types that the Crawler discovered. + +1. **dynamodb** - String indicating that the table can be used for supplemental meta-data by the Athena DynamoDB Connector. This string can be in any one of the following places: + 1. in the table properites/parameters under a field called "classification" (exact match). + 2. in the table's storage descriptor's location field (substring match). + 3. in the table's storage descriptor's parameters under a field called "classification" (exact match). +2. **dynamo-db-flag** - String indicating that the *database* contains tables used for supplemental meta-data by the Athena DynamoDB Connector. This is required for any Glue databases other than "default" +and is useful for filtering out irrelevant databases in accounts that have lots of them. This string should be in the Location URI of the Glue Database (substring match). + + +### Required Permissions + +Review the "Policies" section of the athena-dynamodb.yaml file for full details on the IAM Policies required by this connector. A brief summary is below. + +1. DynamoDB Read Access - The connector uses the DescribeTable, ListSchemas, ListTables, Query, and Scan APIs. +2. S3 Write Access - In order to successfully handle large queries, the connector requires write access to a location in S3. +3. Glue Data Catalog - Since DynamoDB does not have a meta-data store, the connector requires Read-Only access to Glue's DataCatalog for supplemental table schema information. +4. CloudWatch Logs - This is a somewhat implicit permission when deploying a Lambda function but it needs access to cloudwatch logs for storing logs. +1. Athena GetQueryExecution - The connector uses this access to fast-fail when the upstream Athena query has terminated. + +### Deploying The Connector + +To use this connector in your queries, navigate to AWS Serverless Application Repository and deploy a pre-built version of this connector. Alternatively, you can build and deploy this connector from +source follow the below steps or use the more detailed tutorial in the athena-example module: + +1. From the athena-federation-sdk dir, run `mvn clean install` if you haven't already. +2. From the athena-dynamodb dir, run `mvn clean install`. +3. From the athena-dynamodb dir, run `../tools/publish.sh S3_BUCKET_NAME athena-dynamodb` to publish the connector to your private AWS Serverless Application Repository. The S3_BUCKET in the command +is where a copy of the connector's code will be stored for Serverless Application Repository to retrieve it. This will allow users with permission to do so, the ability to deploy instances of the +connector via 1-Click form. Then navigate to [Serverless Application Repository](https://aws.amazon.com/serverless/serverlessrepo) + +## Performance + +The Athena DynamoDB Connector does support parallel scans and will attempt to push down predicates as part of its DynamoDB queries. A hash key predicate with X distinct values will result in X Query +calls to DynamoDB. All other predicate scenarios will results in Y number of Scan calls where Y is heuristically determined based on the size of your table and its provisioned throughput. \ No newline at end of file diff --git a/athena-dynamodb/athena-dynamodb.yaml b/athena-dynamodb/athena-dynamodb.yaml new file mode 100644 index 0000000000..821600a123 --- /dev/null +++ b/athena-dynamodb/athena-dynamodb.yaml @@ -0,0 +1,77 @@ +Transform: 'AWS::Serverless-2016-10-31' +Metadata: + 'AWS::ServerlessRepo::Application': + Name: AthenaDynamoDBConnector + Description: 'This connector enables Amazon Athena to communicate with DynamoDB, making your tables accessible via SQL.' + Author: 'Amazon Athena' + SpdxLicenseId: Apache-2.0 + LicenseUrl: LICENSE.txt + ReadmeUrl: README.md + Labels: + - athena-federation + HomePageUrl: 'https://github.com/awslabs/aws-athena-query-federation' + SemanticVersion: 1.0.0 + SourceCodeUrl: 'https://github.com/awslabs/aws-athena-query-federation' +Parameters: + AthenaCatalogName: + Description: 'The name you will give to this catalog in Athena. It will also be used as the function name.' + Type: String + SpillBucket: + Description: 'The bucket where this function can spill data.' + Type: String + SpillPrefix: + Description: 'The bucket prefix where this function can spill large responses.' + Type: String + Default: athena-spill + LambdaTimeout: + Description: 'Maximum Lambda invocation runtime in seconds. (min 1 - 900 max)' + Default: 900 + Type: Number + LambdaMemory: + Description: 'Lambda memory in MB (min 128 - 3008 max).' + Default: 3008 + Type: Number + DisableSpillEncryption: + Description: "WARNING: If set to 'true' encryption for spilled data is disabled." + Default: 'false' + Type: String +Resources: + ConnectorConfig: + Type: 'AWS::Serverless::Function' + Properties: + Environment: + Variables: + disable_spill_encryption: !Ref DisableSpillEncryption + spill_bucket: !Ref SpillBucket + spill_prefix: !Ref SpillPrefix + FunctionName: !Ref AthenaCatalogName + Handler: "com.amazonaws.athena.connectors.dynamodb.DynamoDBCompositeHandler" + CodeUri: "./target/athena-dynamodb-1.0.jar" + Description: "Enables Amazon Athena to communicate with DynamoDB, making your tables accessible via SQL" + Runtime: java8 + Timeout: !Ref LambdaTimeout + MemorySize: !Ref LambdaMemory + Policies: + - Statement: + - Action: + - dynamodb:DescribeTable + - dynamodb:ListSchemas + - dynamodb:ListTables + - dynamodb:Query + - dynamodb:Scan + - glue:GetTableVersions + - glue:GetPartitions + - glue:GetTables + - glue:GetTableVersion + - glue:GetDatabases + - glue:GetTable + - glue:GetPartition + - glue:GetDatabase + - athena:GetQueryExecution + Effect: Allow + Resource: '*' + Version: '2012-10-17' + #S3CrudPolicy allows our connector to spill large responses to S3. You can optionally replace this pre-made policy + #with one that is more restrictive and can only 'put' but not read,delete, or overwrite files. + - S3CrudPolicy: + BucketName: !Ref SpillBucket \ No newline at end of file diff --git a/athena-dynamodb/native-libs/libsqlite4java-linux-amd64-1.0.392.so b/athena-dynamodb/native-libs/libsqlite4java-linux-amd64-1.0.392.so new file mode 100644 index 0000000000000000000000000000000000000000..884615789b9ca54e9487153e0492a6be5fe550bc GIT binary patch literal 902282 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

Q6(0=X~EJi!@f{eSY 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 z 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

    ;v<9)vqJ=Z()61=fAda{ITd68 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�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

    Xg)`Mc_4Fz9(MG$=6ozt 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 z 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≻^p8>vDGH20Xoh5##1t)GyRkOv;O_+^F62dUc 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

    f@WisTkb4UBp8$qrt2J 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 A*#H0l literal 0 HcmV?d00001 diff --git a/athena-dynamodb/native-libs/libsqlite4java-osx-1.0.392.dylib b/athena-dynamodb/native-libs/libsqlite4java-osx-1.0.392.dylib new file mode 100644 index 0000000000000000000000000000000000000000..0276162614d39f36ae3d5d1c0fadbb5baf08f6d1 GIT binary patch literal 1620880 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


    L(b&APzR2XSuOCIDY? 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

    2Nqj;GWe*{{%+stj=Wx+AyD5OwMG0Pnm!4*h2A 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ƾWwq}We2pjA&dH&Ad4njng1WhnB=aKD4 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%0AS|UaT|pMY+^1?|IV`ZhLOr1G2SQ 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 z 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%0A#&kRH`uA_# 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⪼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�)WJqV&G~wODfWz7P^n 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%0A)$m$FbRQ7?-BbJ_n`S7Xk#dkj5mxw{| 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 WAxJ>6jMU;w0`5zaTo6my&;J9B>}L4@ literal 0 HcmV?d00001 diff --git a/athena-dynamodb/native-libs/sqlite4java-win32-x64-1.0.392.dll b/athena-dynamodb/native-libs/sqlite4java-win32-x64-1.0.392.dll new file mode 100644 index 0000000000000000000000000000000000000000..70d258f29bc4ecd0d00c93ec6544492eb1651b65 GIT binary patch literal 684032 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�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%0A~nCOa&eq5#+&)%$nz;=FjHE>Syf0x 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 z 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

N6>N;BVq)D#0WE$DJ6D^JB%Dn0VW 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 +6A3d6oh1#ova9Y(*njXjB30>N5Oa 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%0DA%2>vlE?@G7AFddS;=rvbHq!5bj21HJ_K-oU&8 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 + + + aws-athena-query-federation + com.amazonaws + 1.0 + + 4.0.0 + + athena-dynamodb + + + com.amazonaws + aws-athena-federation-sdk + ${aws-athena-federation-sdk.version} + + + com.amazonaws + aws-java-sdk-dynamodb + ${aws-sdk.version} + + + com.amazonaws + DynamoDBLocal + 1.11.477 + test + + + + org.hamcrest + hamcrest + 2.1 + test + + + + + + dynamodb-local-oregon + DynamoDB Local Release Repository + https://s3-us-west-2.amazonaws.com/dynamodb-local/release + + false + + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + 2.10 + + + copy + test-compile + + copy-dependencies + + + test + so,dll,dylib + ${project.basedir}/native-libs + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.1 + + false + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + package + + shade + + + + + + + \ No newline at end of file diff --git a/athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/DynamoDBCompositeHandler.java b/athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/DynamoDBCompositeHandler.java new file mode 100644 index 0000000000..2278e3315c --- /dev/null +++ b/athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/DynamoDBCompositeHandler.java @@ -0,0 +1,35 @@ +/*- + * #%L + * athena-dynamodb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.dynamodb; + +import com.amazonaws.athena.connector.lambda.handlers.CompositeHandler; + +/** + * Boilerplate composite handler that allows us to use a single Lambda function for both + * Metadata and Data. In this case we just compose DynamoDBMetadataHandler and DynamoDBRecordHandler. + */ +public class DynamoDBCompositeHandler + extends CompositeHandler +{ + public DynamoDBCompositeHandler() + { + super(new DynamoDBMetadataHandler(), new DynamoDBRecordHandler()); + } +} diff --git a/athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/DynamoDBMetadataHandler.java b/athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/DynamoDBMetadataHandler.java new file mode 100644 index 0000000000..c3db4e90e1 --- /dev/null +++ b/athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/DynamoDBMetadataHandler.java @@ -0,0 +1,467 @@ +/*- + * #%L + * athena-dynamodb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.dynamodb; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.ThrottlingInvoker; +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockWriter; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import com.amazonaws.athena.connector.lambda.domain.spill.SpillLocation; +import com.amazonaws.athena.connector.lambda.handlers.GlueMetadataHandler; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesResponse; +import com.amazonaws.athena.connector.lambda.metadata.glue.GlueFieldLexer; +import com.amazonaws.athena.connector.lambda.security.EncryptionKeyFactory; +import com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants; +import com.amazonaws.athena.connectors.dynamodb.model.DynamoDBTable; +import com.amazonaws.athena.connectors.dynamodb.resolver.DynamoDBTableResolver; +import com.amazonaws.athena.connectors.dynamodb.util.DDBPredicateUtils; +import com.amazonaws.athena.connectors.dynamodb.util.DDBTableUtils; +import com.amazonaws.athena.connectors.dynamodb.util.DDBTypeUtils; +import com.amazonaws.athena.connectors.dynamodb.util.IncrementingValueNameProducer; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; +import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder; +import com.amazonaws.services.dynamodbv2.document.ItemUtils; +import com.amazonaws.services.dynamodbv2.model.AttributeValue; +import com.amazonaws.services.glue.AWSGlue; +import com.amazonaws.services.glue.AWSGlueClientBuilder; +import com.amazonaws.services.glue.model.Database; +import com.amazonaws.services.glue.model.Table; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.amazonaws.util.json.Jackson; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableList; +import org.apache.arrow.vector.complex.reader.FieldReader; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.arrow.vector.types.pojo.Schema; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.TimeoutException; + +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.DEFAULT_SCHEMA; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.EXPRESSION_NAMES_METADATA; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.EXPRESSION_VALUES_METADATA; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.HASH_KEY_NAME_METADATA; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.INDEX_METADATA; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.NON_KEY_FILTER_METADATA; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.PARTITION_TYPE_METADATA; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.QUERY_PARTITION_TYPE; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.RANGE_KEY_FILTER_METADATA; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.RANGE_KEY_NAME_METADATA; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.SCAN_PARTITION_TYPE; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.SEGMENT_COUNT_METADATA; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.SEGMENT_ID_PROPERTY; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.TABLE_METADATA; +import static com.amazonaws.athena.connectors.dynamodb.throttling.DynamoDBExceptionFilter.EXCEPTION_FILTER; + +/** + * Handles metadata requests for the Athena DynamoDB Connector. + *

+ * For more detail, please see the module's README.md, some notable characteristics of this class include: + *

+ * 1. Glue DataCatalog is used for schema information by default unless disabled. If disabled or the table
+ * is not found, it falls back to doing a small table scan and derives a schema from that.
+ * 2. Determines if the data splits will need to perform DDB Queries or Scans.
+ * 3. Splits up the hash key into distinct Query splits if possible, otherwise falls back to creating Scan splits.
+ * 4. Also determines the best index to use (if available) if the available predicates align with Key Attributes.
+ * 5. Creates scan splits that support Parallel Scan and tries to choose the optimal number of splits.
+ * 6. Pushes down all other predicates into ready-to-use filter expressions to pass to DDB. + */ +public class DynamoDBMetadataHandler + extends GlueMetadataHandler +{ + @VisibleForTesting + static final int MAX_SPLITS_PER_REQUEST = 1000; + private static final Logger logger = LoggerFactory.getLogger(DynamoDBMetadataHandler.class); + static final String DYNAMODB = "dynamodb"; + private static final String sourceType = "ddb"; + private static final String GLUE_ENV = "disable_glue"; + // defines the value that should be present in the Glue Database URI to enable the DB for DynamoDB. + static final String DYNAMO_DB_FLAG = "dynamo-db-flag"; + // used to filter out Glue tables which lack indications of being used for DDB. + private static final TableFilter TABLE_FILTER = (Table table) -> table.getStorageDescriptor().getLocation().contains(DYNAMODB) + || (table.getParameters() != null && DYNAMODB.equals(table.getParameters().get("classification"))) + || (table.getStorageDescriptor().getParameters() != null && DYNAMODB.equals(table.getStorageDescriptor().getParameters().get("classification"))); + // used to filter out Glue databases which lack the DYNAMO_DB_FLAG in the URI. + private static final DatabaseFilter DB_FILTER = (Database database) -> (database.getLocationUri() != null && database.getLocationUri().contains(DYNAMO_DB_FLAG)) + || DEFAULT_SCHEMA.equals(database.getName()); + + private final ThrottlingInvoker invoker = ThrottlingInvoker.newDefaultBuilder(EXCEPTION_FILTER).build(); + private final AmazonDynamoDB ddbClient; + private final AWSGlue glueClient; + private final DynamoDBTableResolver tableResolver; + + public DynamoDBMetadataHandler() + { + super((System.getenv(GLUE_ENV) == null || !Boolean.parseBoolean(System.getenv(GLUE_ENV))) ? AWSGlueClientBuilder.standard().build() : null, + sourceType); + ddbClient = AmazonDynamoDBClientBuilder.standard().build(); + glueClient = getAwsGlue(); + tableResolver = new DynamoDBTableResolver(invoker, ddbClient); + } + + @VisibleForTesting + DynamoDBMetadataHandler(EncryptionKeyFactory keyFactory, + AWSSecretsManager secretsManager, + AmazonAthena athena, + String spillBucket, + String spillPrefix, + AmazonDynamoDB ddbClient, + AWSGlue glueClient) + { + super(glueClient, keyFactory, secretsManager, athena, sourceType, spillBucket, spillPrefix); + this.glueClient = glueClient; + this.ddbClient = ddbClient; + this.tableResolver = new DynamoDBTableResolver(invoker, ddbClient); + } + + /** + * Since DynamoDB does not have "schemas" or "databases", this lists all the Glue databases (if not + * disabled) that contain {@value #DYNAMO_DB_FLAG} in their URIs . Otherwise returns just a "default" schema. + * + * @see GlueMetadataHandler + */ + @Override + public ListSchemasResponse doListSchemaNames(BlockAllocator allocator, ListSchemasRequest request) + throws Exception + { + if (glueClient != null) { + try { + return super.doListSchemaNames(allocator, request, DB_FILTER); + } + catch (RuntimeException e) { + logger.warn("doListSchemaNames: Unable to retrieve schemas from AWSGlue.", e); + } + } + + return new ListSchemasResponse(request.getCatalogName(), ImmutableList.of("default")); + } + + /** + * Lists all Glue tables (if not disabled) in the schema specified that indicate use for DynamoDB metadata. + * Indications for DynamoDB use in Glue are:
+ * 1. The top level table properties/parameters contains a key called "classification" with value {@value #DYNAMODB}.
+ * 2. Or the storage descriptor's location field contains {@value #DYNAMODB}.
+ * 3. Or the storage descriptor has a parameter called "classification" with value {@value #DYNAMODB}. + *

+ * If the specified schema is "default", this also returns an intersection with actual tables in DynamoDB. + * + * @see GlueMetadataHandler + */ + @Override + public ListTablesResponse doListTables(BlockAllocator allocator, ListTablesRequest request) + throws Exception + { + // LinkedHashSet for consistent ordering + Set combinedTables = new LinkedHashSet<>(); + if (glueClient != null) { + try { + // does not validate that the tables are actually DDB tables + combinedTables.addAll(super.doListTables(allocator, request, TABLE_FILTER).getTables()); + } + catch (RuntimeException e) { + logger.warn("doListTables: Unable to retrieve tables from AWSGlue in database/schema {}", request.getSchemaName(), e); + } + } + + // add tables that may not be in Glue (if listing the default schema) + if (DynamoDBConstants.DEFAULT_SCHEMA.equals(request.getSchemaName())) { + combinedTables.addAll(tableResolver.listTables()); + } + return new ListTablesResponse(request.getCatalogName(), new ArrayList<>(combinedTables)); + } + + /** + * Fetches a table's schema from Glue DataCatalog if present and not disabled, otherwise falls + * back to doing a small table scan derives a schema from that. + * + * @see GlueMetadataHandler + */ + @Override + public GetTableResponse doGetTable(BlockAllocator allocator, GetTableRequest request) + throws Exception + { + if (glueClient != null) { + try { + // does not validate that the table is actually a DDB table + return super.doGetTable(allocator, request); + } + catch (RuntimeException e) { + logger.debug("doGetTable: Unable to retrieve table {} from AWSGlue in database/schema {}", request.getTableName().getSchemaName(), e); + } + } + + // ignore database/schema name since there are no databases/schemas in DDB + Schema schema = tableResolver.getTableSchema(request.getTableName().getTableName()); + return new GetTableResponse(request.getCatalogName(), request.getTableName(), schema); + } + + /** + * Generates a partition schema with metadata derived from available predicates. This metadata will be + * copied to splits in the #doGetSplits call. At this point it is determined whether we can partition + * by hash key or fall back to a full table scan. + * + * @see GlueMetadataHandler + */ + @Override + public void enhancePartitionSchema(SchemaBuilder partitionSchemaBuilder, GetTableLayoutRequest request) + { + DynamoDBTable table = null; + try { + table = tableResolver.getTableMetadata(request.getTableName().getTableName()); + } + catch (TimeoutException e) { + throw new RuntimeException(e); + } + // add table name so we don't have to do case insensitive resolution again + partitionSchemaBuilder.addMetadata(TABLE_METADATA, table.getName()); + Map summary = request.getConstraints().getSummary(); + DynamoDBTable index = DDBPredicateUtils.getBestIndexForPredicates(table, summary); + String hashKeyName = index.getHashKey(); + ValueSet hashKeyValueSet = summary.get(hashKeyName); + List hashKeyValues = (hashKeyValueSet != null) ? DDBPredicateUtils.getHashKeyAttributeValues(hashKeyValueSet) : Collections.emptyList(); + + Set alreadyFilteredColumns = new HashSet<>(); + List valueAccumulator = new ArrayList<>(); + IncrementingValueNameProducer valueNameProducer = new IncrementingValueNameProducer(); + if (!hashKeyValues.isEmpty()) { + // can "partition" on hash key + partitionSchemaBuilder.addField(hashKeyName, hashKeyValueSet.getType()); + partitionSchemaBuilder.addMetadata(HASH_KEY_NAME_METADATA, hashKeyName); + alreadyFilteredColumns.add(hashKeyName); + partitionSchemaBuilder.addMetadata(PARTITION_TYPE_METADATA, QUERY_PARTITION_TYPE); + if (!table.equals(index)) { + partitionSchemaBuilder.addMetadata(INDEX_METADATA, index.getName()); + } + + // add range key filter if there is one + Optional rangeKey = index.getRangeKey(); + if (rangeKey.isPresent()) { + String rangeKeyName = rangeKey.get(); + if (summary.containsKey(rangeKeyName)) { + String rangeKeyFilter = DDBPredicateUtils.generateSingleColumnFilter(rangeKeyName, summary.get(rangeKeyName), valueAccumulator, valueNameProducer); + partitionSchemaBuilder.addMetadata(RANGE_KEY_NAME_METADATA, rangeKeyName); + partitionSchemaBuilder.addMetadata(RANGE_KEY_FILTER_METADATA, rangeKeyFilter); + alreadyFilteredColumns.add(rangeKeyName); + } + } + } + else { + // always fall back to a scan + partitionSchemaBuilder.addField(SEGMENT_COUNT_METADATA, Types.MinorType.INT.getType()); + partitionSchemaBuilder.addMetadata(PARTITION_TYPE_METADATA, SCAN_PARTITION_TYPE); + } + + precomputeAdditionalMetadata(alreadyFilteredColumns, summary, valueAccumulator, valueNameProducer, partitionSchemaBuilder); + } + + /** + * Generates hash key partitions if possible or generates a single partition with the heuristically + * determined optimal scan segment count specified inside of it + * + * @see GlueMetadataHandler + */ + @Override + public void getPartitions(BlockWriter blockWriter, GetTableLayoutRequest request, QueryStatusChecker queryStatusChecker) + throws Exception + { + // TODO consider caching this repeated work in #enhancePartitionSchema + DynamoDBTable table = tableResolver.getTableMetadata(request.getTableName().getTableName()); + Map summary = request.getConstraints().getSummary(); + DynamoDBTable index = DDBPredicateUtils.getBestIndexForPredicates(table, summary); + String hashKeyName = index.getHashKey(); + ValueSet hashKeyValueSet = summary.get(hashKeyName); + List hashKeyValues = (hashKeyValueSet != null) ? DDBPredicateUtils.getHashKeyAttributeValues(hashKeyValueSet) : Collections.emptyList(); + + if (!hashKeyValues.isEmpty()) { + for (Object hashKeyValue : hashKeyValues) { + blockWriter.writeRows((Block block, int rowNum) -> { + block.setValue(hashKeyName, rowNum, hashKeyValue); + //we added 1 partition per hashkey value + return 1; + }); + } + } + else { + // always fall back to a scan, need to return at least one partition so stick the segment count in it + int segmentCount = DDBTableUtils.getNumSegments(table.getProvisionedReadCapacity(), table.getApproxTableSizeInBytes()); + blockWriter.writeRows((Block block, int rowNum) -> { + block.setValue(SEGMENT_COUNT_METADATA, rowNum, segmentCount); + return 1; + }); + } + } + + /* + Injects additional metadata into the partition schema like a non-key filter expression for additional DDB-side filtering + */ + private void precomputeAdditionalMetadata(Set columnsToIgnore, Map predicates, List accumulator, + IncrementingValueNameProducer valueNameProducer, SchemaBuilder partitionsSchemaBuilder) + { + // precompute non-key filter + String filterExpression = DDBPredicateUtils.generateFilterExpression(columnsToIgnore, predicates, accumulator, valueNameProducer); + if (filterExpression != null) { + partitionsSchemaBuilder.addMetadata(NON_KEY_FILTER_METADATA, filterExpression); + } + + if (!accumulator.isEmpty()) { + // add in mappings for aliased columns and value placeholders + Map aliasedColumns = new HashMap<>(); + for (String column : predicates.keySet()) { + aliasedColumns.put(DDBPredicateUtils.aliasColumn(column), column); + } + Map expressionValueMapping = new HashMap<>(); + // IncrementingValueNameProducer is repeatable for simplicity + IncrementingValueNameProducer valueNameProducer2 = new IncrementingValueNameProducer(); + for (AttributeValue value : accumulator) { + expressionValueMapping.put(valueNameProducer2.getNext(), value); + } + partitionsSchemaBuilder.addMetadata(EXPRESSION_NAMES_METADATA, Jackson.toJsonString(aliasedColumns)); + partitionsSchemaBuilder.addMetadata(EXPRESSION_VALUES_METADATA, Jackson.toJsonString(expressionValueMapping)); + } + } + + /** + * Copies data from partitions and creates splits, serializing as necessary for later calls to RecordHandler#readWithContraint. + * This API supports pagination. + * + * @see GlueMetadataHandler + */ + @Override + public GetSplitsResponse doGetSplits(BlockAllocator allocator, GetSplitsRequest request) + { + int partitionContd = decodeContinuationToken(request); + Set splits = new HashSet<>(); + Block partitions = request.getPartitions(); + Map partitionMetadata = partitions.getSchema().getCustomMetadata(); + // copy all partition metadata to the split + Map splitMetadata = new HashMap<>(partitionMetadata); + String partitionType = partitionMetadata.get(PARTITION_TYPE_METADATA); + if (partitionType == null) { + throw new IllegalStateException(String.format("No metadata %s defined in Schema %s", PARTITION_TYPE_METADATA, partitions.getSchema())); + } + if (QUERY_PARTITION_TYPE.equals(partitionType)) { + String hashKeyName = partitionMetadata.get(HASH_KEY_NAME_METADATA); + FieldReader hashKeyValueReader = partitions.getFieldReader(hashKeyName); + // one split per hash key value (since one DDB query can only take one hash key value) + for (int curPartition = partitionContd; curPartition < partitions.getRowCount(); curPartition++) { + hashKeyValueReader.setPosition(curPartition); + + //Every split must have a unique location if we wish to spill to avoid failures + SpillLocation spillLocation = makeSpillLocation(request); + + Object hashKeyValue = DDBTypeUtils.convertArrowTypeIfNecessary(hashKeyValueReader.readObject()); + String hashKeyValueJSON = Jackson.toJsonString(ItemUtils.toAttributeValue(hashKeyValue)); + splitMetadata.put(hashKeyName, hashKeyValueJSON); + + splitMetadata.put(SEGMENT_COUNT_METADATA, String.valueOf(partitions.getRowCount())); + + splits.add(new Split(spillLocation, makeEncryptionKey(), splitMetadata)); + + if (splits.size() == MAX_SPLITS_PER_REQUEST && curPartition != partitions.getRowCount() - 1) { + // We've reached max page size and this is not the last partition + // so send the page back + return new GetSplitsResponse(request.getCatalogName(), + splits, + encodeContinuationToken(curPartition)); + } + } + return new GetSplitsResponse(request.getCatalogName(), splits, null); + } + else if (SCAN_PARTITION_TYPE.equals(partitionType)) { + FieldReader segmentCountReader = partitions.getFieldReader(SEGMENT_COUNT_METADATA); + int segmentCount = segmentCountReader.readInteger(); + for (int curPartition = partitionContd; curPartition < segmentCount; curPartition++) { + SpillLocation spillLocation = makeSpillLocation(request); + + splitMetadata.put(SEGMENT_ID_PROPERTY, String.valueOf(curPartition)); + splitMetadata.put(SEGMENT_COUNT_METADATA, String.valueOf(segmentCount)); + + splits.add(new Split(spillLocation, makeEncryptionKey(), splitMetadata)); + + if (splits.size() == MAX_SPLITS_PER_REQUEST && curPartition != segmentCount - 1) { + // We've reached max page size and this is not the last partition + // so send the page back + return new GetSplitsResponse(request.getCatalogName(), + splits, + encodeContinuationToken(curPartition)); + } + } + return new GetSplitsResponse(request.getCatalogName(), splits, null); + } + else { + throw new IllegalStateException("Unexpected partition type " + partitionType); + } + } + + /** + * @see GlueMetadataHandler + */ + @Override + protected Field convertField(String name, String glueType) + { + return GlueFieldLexer.lex(name, glueType); + } + + /* + Used to handle paginated requests. Returns the partition number to resume with. + */ + private int decodeContinuationToken(GetSplitsRequest request) + { + if (request.hasContinuationToken()) { + return Integer.valueOf(request.getContinuationToken()) + 1; + } + + //No continuation token present + return 0; + } + + /* + Used to create pagination tokens by encoding the number of the next partition to process. + */ + private String encodeContinuationToken(int partition) + { + return String.valueOf(partition); + } +} diff --git a/athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/DynamoDBRecordHandler.java b/athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/DynamoDBRecordHandler.java new file mode 100644 index 0000000000..e4b5856969 --- /dev/null +++ b/athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/DynamoDBRecordHandler.java @@ -0,0 +1,327 @@ +/*- + * #%L + * athena-dynamodb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.dynamodb; + +import com.amazonaws.AmazonWebServiceRequest; +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.ThrottlingInvoker; +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockSpiller; +import com.amazonaws.athena.connector.lambda.data.FieldResolver; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.handlers.RecordHandler; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.athena.connectors.dynamodb.util.DDBPredicateUtils; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; +import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder; +import com.amazonaws.services.dynamodbv2.document.ItemUtils; +import com.amazonaws.services.dynamodbv2.model.AttributeValue; +import com.amazonaws.services.dynamodbv2.model.QueryRequest; +import com.amazonaws.services.dynamodbv2.model.QueryResult; +import com.amazonaws.services.dynamodbv2.model.ScanRequest; +import com.amazonaws.services.dynamodbv2.model.ScanResult; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.amazonaws.util.json.Jackson; +import com.fasterxml.jackson.core.type.TypeReference; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import org.apache.arrow.util.VisibleForTesting; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Field; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; + +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.EXPRESSION_NAMES_METADATA; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.EXPRESSION_VALUES_METADATA; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.HASH_KEY_NAME_METADATA; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.INDEX_METADATA; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.NON_KEY_FILTER_METADATA; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.RANGE_KEY_FILTER_METADATA; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.SEGMENT_COUNT_METADATA; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.SEGMENT_ID_PROPERTY; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.TABLE_METADATA; +import static com.amazonaws.athena.connectors.dynamodb.throttling.DynamoDBExceptionFilter.EXCEPTION_FILTER; +import static com.google.common.base.Preconditions.checkArgument; + +/** + * Handles data read record requests for the Athena DynamoDB Connector. + *

+ * For more detail, please see the module's README.md, some notable characteristics of this class include: + *

+ * 1. Reads and maps DynamoDB data for a specific split. The split can either represent a single hash key + * or a table scan segment.
+ * 2. Attempts to push down all predicates into DynamoDB to reduce read cost and bytes over the wire. + */ +public class DynamoDBRecordHandler + extends RecordHandler +{ + private static final Logger logger = LoggerFactory.getLogger(DynamoDBRecordHandler.class); + private static final String sourceType = "ddb"; + + private static final String HASH_KEY_VALUE_ALIAS = ":hashKeyValue"; + + private static final TypeReference> STRING_MAP_TYPE_REFERENCE = new TypeReference>() {}; + private static final TypeReference> ATTRIBUTE_VALUE_MAP_TYPE_REFERENCE = new TypeReference>() {}; + + private final LoadingCache invokerCache = CacheBuilder.newBuilder().build( + new CacheLoader() { + @Override + public ThrottlingInvoker load(String tableName) + throws Exception + { + return ThrottlingInvoker.newDefaultBuilder(EXCEPTION_FILTER).build(); + } + }); + private final AmazonDynamoDB ddbClient; + + public DynamoDBRecordHandler() + { + super(sourceType); + this.ddbClient = AmazonDynamoDBClientBuilder.standard().build(); + } + + @VisibleForTesting + DynamoDBRecordHandler(AmazonDynamoDB ddbClient, AmazonS3 amazonS3, AWSSecretsManager secretsManager, AmazonAthena athena, String sourceType) + { + super(amazonS3, secretsManager, athena, sourceType); + this.ddbClient = ddbClient; + } + + /** + * Reads data from DynamoDB by submitting either a Query or a Scan, depending + * on the type of split, and includes any filters specified in the split. + * + * @see RecordHandler + */ + @Override + protected void readWithConstraint(BlockSpiller spiller, ReadRecordsRequest recordsRequest, QueryStatusChecker queryStatusChecker) + throws ExecutionException + { + Split split = recordsRequest.getSplit(); + // use the property instead of the request table name because of case sensitivity + String tableName = split.getProperty(TABLE_METADATA); + invokerCache.get(tableName).setBlockSpiller(spiller); + Iterator> itemIterator = getIterator(split, tableName); + + long numRows = 0; + AtomicLong numResultRows = new AtomicLong(0); + while (itemIterator.hasNext()) { + if (!queryStatusChecker.isQueryRunning()) { + // we can stop processing because the query waiting for this data has already terminated + return; + } + numRows++; + spiller.writeRows((Block block, int rowNum) -> { + Map item = itemIterator.next(); + if (item == null) { + // this can happen regardless of the hasNext() check above for the very first iteration since itemIterator + // had not made any DDB calls yet and there may be zero items returned when it does + return 0; + } + + boolean matched = true; + numResultRows.getAndIncrement(); + for (Field nextField : recordsRequest.getSchema().getFields()) { + Object value = ItemUtils.toSimpleValue(item.get(nextField.getName())); + Types.MinorType fieldType = Types.getMinorTypeForArrowType(nextField.getType()); + try { + switch (fieldType) { + case LIST: + // DDB may return Set so coerce to List + List valueAsList = value != null ? new ArrayList((Collection) value) : null; + matched &= block.offerComplexValue(nextField.getName(), + rowNum, + FieldResolver.DEFAULT, + valueAsList); + break; + case STRUCT: + matched &= block.offerComplexValue(nextField.getName(), + rowNum, + (Field field, Object val) -> ((Map) val).get(field.getName()), value); + break; + default: + matched &= block.offerValue(nextField.getName(), rowNum, value); + break; + } + + if (!matched) { + return 0; + } + } + catch (Exception ex) { + throw new RuntimeException("Error while processing field " + nextField.getName(), ex); + } + } + return 1; + }); + } + + logger.info("readWithConstraint: numRows[{}] numResultRows[{}]", numRows, numResultRows.get()); + } + + /* + Converts a split into a Query or Scan request + */ + private AmazonWebServiceRequest buildReadRequest(Split split, String tableName) + { + validateExpectedMetadata(split.getProperties()); + // prepare filters + String rangeKeyFilter = split.getProperty(RANGE_KEY_FILTER_METADATA); + String nonKeyFilter = split.getProperty(NON_KEY_FILTER_METADATA); + Map expressionAttributeNames = new HashMap<>(); + Map expressionAttributeValues = new HashMap<>(); + if (rangeKeyFilter != null || nonKeyFilter != null) { + try { + expressionAttributeNames.putAll(Jackson.getObjectMapper().readValue(split.getProperty(EXPRESSION_NAMES_METADATA), STRING_MAP_TYPE_REFERENCE)); + expressionAttributeValues.putAll(Jackson.getObjectMapper().readValue(split.getProperty(EXPRESSION_VALUES_METADATA), ATTRIBUTE_VALUE_MAP_TYPE_REFERENCE)); + } + catch (IOException e) { + throw new RuntimeException(e); + } + } + + boolean isQuery = split.getProperty(SEGMENT_ID_PROPERTY) == null; + + if (isQuery) { + // prepare key condition expression + String indexName = split.getProperty(INDEX_METADATA); + String hashKeyName = split.getProperty(HASH_KEY_NAME_METADATA); + String hashKeyAlias = DDBPredicateUtils.aliasColumn(hashKeyName); + String keyConditionExpression = hashKeyAlias + " = " + HASH_KEY_VALUE_ALIAS; + if (rangeKeyFilter != null) { + keyConditionExpression += " AND " + rangeKeyFilter; + } + expressionAttributeNames.put(hashKeyAlias, hashKeyName); + expressionAttributeValues.put(HASH_KEY_VALUE_ALIAS, Jackson.fromJsonString(split.getProperty(hashKeyName), AttributeValue.class)); + + return new QueryRequest() + .withTableName(tableName) + .withIndexName(indexName) + .withKeyConditionExpression(keyConditionExpression) + .withFilterExpression(nonKeyFilter) + .withExpressionAttributeNames(expressionAttributeNames) + .withExpressionAttributeValues(expressionAttributeValues); + } + else { + int segmentId = Integer.parseInt(split.getProperty(SEGMENT_ID_PROPERTY)); + int segmentCount = Integer.parseInt(split.getProperty(SEGMENT_COUNT_METADATA)); + + return new ScanRequest() + .withTableName(tableName) + .withSegment(segmentId) + .withTotalSegments(segmentCount) + .withFilterExpression(nonKeyFilter) + .withExpressionAttributeNames(expressionAttributeNames.isEmpty() ? null : expressionAttributeNames) + .withExpressionAttributeValues(expressionAttributeValues.isEmpty() ? null : expressionAttributeValues); + } + } + + /* + Creates an iterator that can iterate through a Query or Scan, sending paginated requests as necessary + */ + private Iterator> getIterator(Split split, String tableName) + { + AmazonWebServiceRequest request = buildReadRequest(split, tableName); + return new Iterator>() { + AtomicReference> lastKeyEvaluated = new AtomicReference<>(); + AtomicReference>> currentPageIterator = new AtomicReference<>(); + + @Override + public boolean hasNext() + { + return currentPageIterator.get() == null + || currentPageIterator.get().hasNext() + || lastKeyEvaluated.get() != null; + } + + @Override + public Map next() + { + if (currentPageIterator.get() != null && currentPageIterator.get().hasNext()) { + return currentPageIterator.get().next(); + } + Iterator> iterator; + try { + if (request instanceof QueryRequest) { + QueryRequest paginatedRequest = ((QueryRequest) request).withExclusiveStartKey(lastKeyEvaluated.get()); + if (logger.isDebugEnabled()) { + logger.debug("Invoking DDB with Query request: {}", request); + } + QueryResult queryResult = invokerCache.get(tableName).invoke(() -> ddbClient.query(paginatedRequest)); + lastKeyEvaluated.set(queryResult.getLastEvaluatedKey()); + iterator = queryResult.getItems().iterator(); + } + else { + ScanRequest paginatedRequest = ((ScanRequest) request).withExclusiveStartKey(lastKeyEvaluated.get()); + if (logger.isDebugEnabled()) { + logger.debug("Invoking DDB with Scan request: {}", request); + } + ScanResult scanResult = invokerCache.get(tableName).invoke(() -> ddbClient.scan(paginatedRequest)); + lastKeyEvaluated.set(scanResult.getLastEvaluatedKey()); + iterator = scanResult.getItems().iterator(); + } + } + catch (TimeoutException | ExecutionException e) { + throw new RuntimeException(e); + } + currentPageIterator.set(iterator); + if (iterator.hasNext()) { + return iterator.next(); + } + else { + return null; + } + } + }; + } + + /* + Validates that the required metadata is present for split processing + */ + private void validateExpectedMetadata(Map metadata) + { + boolean isQuery = !metadata.containsKey(SEGMENT_ID_PROPERTY); + if (isQuery) { + checkArgument(metadata.containsKey(HASH_KEY_NAME_METADATA), "Split missing expected metadata [%s]", HASH_KEY_NAME_METADATA); + } + else { + checkArgument(metadata.containsKey(SEGMENT_COUNT_METADATA), "Split missing expected metadata [%s]", SEGMENT_COUNT_METADATA); + } + if (metadata.containsKey(RANGE_KEY_FILTER_METADATA) || metadata.containsKey(NON_KEY_FILTER_METADATA)) { + checkArgument(metadata.containsKey(EXPRESSION_NAMES_METADATA), "Split missing expected metadata [%s] when filters are present", EXPRESSION_NAMES_METADATA); + checkArgument(metadata.containsKey(EXPRESSION_VALUES_METADATA), "Split missing expected metadata [%s] when filters are present", EXPRESSION_VALUES_METADATA); + } + } +} diff --git a/athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/constants/DynamoDBConstants.java b/athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/constants/DynamoDBConstants.java new file mode 100644 index 0000000000..62587d76af --- /dev/null +++ b/athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/constants/DynamoDBConstants.java @@ -0,0 +1,40 @@ +/*- + * #%L + * athena-dynamodb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.dynamodb.constants; + +public final class DynamoDBConstants +{ + private DynamoDBConstants() {} + + public static final String DEFAULT_SCHEMA = "default"; + public static final String PARTITION_TYPE_METADATA = "partitionType"; + public static final String QUERY_PARTITION_TYPE = "query"; + public static final String SCAN_PARTITION_TYPE = "scan"; + public static final String SEGMENT_COUNT_METADATA = "segmentCount"; + public static final String SEGMENT_ID_PROPERTY = "segmentId"; + public static final String TABLE_METADATA = "table"; + public static final String INDEX_METADATA = "index"; + public static final String HASH_KEY_NAME_METADATA = "hashKeyName"; + public static final String RANGE_KEY_NAME_METADATA = "rangeKeyName"; + public static final String RANGE_KEY_FILTER_METADATA = "rangeKeyFilter"; + public static final String NON_KEY_FILTER_METADATA = "nonKeyFilter"; + public static final String EXPRESSION_NAMES_METADATA = "expressionAttributeNames"; + public static final String EXPRESSION_VALUES_METADATA = "expressionAttributeValues"; +} diff --git a/athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/model/DynamoDBTable.java b/athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/model/DynamoDBTable.java new file mode 100644 index 0000000000..29b9c96ba8 --- /dev/null +++ b/athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/model/DynamoDBTable.java @@ -0,0 +1,122 @@ +/*- + * #%L + * athena-dynamodb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.dynamodb.model; + +import com.amazonaws.services.dynamodbv2.model.AttributeDefinition; +import com.google.common.collect.ImmutableList; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Strings.isNullOrEmpty; +import static java.util.Objects.requireNonNull; + +/** + * A model class to store table metadata in an easy to consume manner. + */ +public class DynamoDBTable +{ + private final String name; + private final String hashKey; + private final Optional rangeKey; + private final List knownAttributeDefinitions; + private final List indexes; + private final long approxTableSizeInBytes; + private final long approxItemCount; + private final long provisionedReadCapacity; + + public DynamoDBTable( + String name, + String hashKey, + Optional rangeKey, + List knownAttributeDefinitions, + List indexes, + long approxTableSizeInBytes, + long approxItemCount, + long provisionedReadCapacity) + { + checkArgument(!isNullOrEmpty(name), "name is null or is empty"); + this.hashKey = requireNonNull(hashKey, "hashKey is null"); + this.rangeKey = requireNonNull(rangeKey, "rangeKey is null"); + this.knownAttributeDefinitions = requireNonNull(knownAttributeDefinitions, "knownAttributeDefinitions is null"); + this.name = requireNonNull(name, "name is null"); + this.indexes = ImmutableList.copyOf(requireNonNull(indexes, "indexes is null")); + this.approxTableSizeInBytes = approxTableSizeInBytes; + this.approxItemCount = approxItemCount; + this.provisionedReadCapacity = provisionedReadCapacity; + } + + public String getName() + { + return name; + } + + public String getHashKey() + { + return hashKey; + } + + public Optional getRangeKey() + { + return rangeKey; + } + + public List getKnownAttributeDefinitions() + { + return knownAttributeDefinitions; + } + + public List getIndexes() + { + return indexes; + } + + public long getApproxTableSizeInBytes() + { + return approxTableSizeInBytes; + } + + public long getProvisionedReadCapacity() + { + return provisionedReadCapacity; + } + + @Override + public int hashCode() + { + return Objects.hash(name); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) { + return true; + } + if ((obj == null) || (getClass() != obj.getClass())) { + return false; + } + + DynamoDBTable other = (DynamoDBTable) obj; + return Objects.equals(this.name, other.name); + } +} diff --git a/athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/resolver/DynamoDBTableResolver.java b/athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/resolver/DynamoDBTableResolver.java new file mode 100644 index 0000000000..d1eb433d14 --- /dev/null +++ b/athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/resolver/DynamoDBTableResolver.java @@ -0,0 +1,163 @@ +/*- + * #%L + * athena-dynamodb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.dynamodb.resolver; + +import com.amazonaws.athena.connector.lambda.ThrottlingInvoker; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connectors.dynamodb.model.DynamoDBTable; +import com.amazonaws.athena.connectors.dynamodb.util.DDBTableUtils; +import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; +import com.amazonaws.services.dynamodbv2.model.ListTablesRequest; +import com.amazonaws.services.dynamodbv2.model.ListTablesResult; +import com.amazonaws.services.dynamodbv2.model.ResourceNotFoundException; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import org.apache.arrow.vector.types.pojo.Schema; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Locale; +import java.util.Optional; +import java.util.concurrent.TimeoutException; + +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.DEFAULT_SCHEMA; +import static com.google.common.collect.ImmutableList.toImmutableList; + +/** + * This class helps with resolving the differences in casing between DynamoDB and Presto. Presto expects all + * databases, tables, and columns to be lower case. This class allows us to resolve DynamoDB tables + * which may have captial letters in them without issue. It does so by fetching all table names and doing + * a case insensitive search over them. It will first try to do a targeted get to reduce the penalty for + * tables which don't have capitalization. + * + * TODO add caching + */ +public class DynamoDBTableResolver +{ + private static final Logger logger = LoggerFactory.getLogger(DynamoDBTableResolver.class); + + private AmazonDynamoDB ddbClient; + // used to handle Throttling events using an AIMD strategy for congestion control. + private ThrottlingInvoker invoker; + + public DynamoDBTableResolver(ThrottlingInvoker invoker, AmazonDynamoDB ddbClient) + { + this.invoker = invoker; + this.ddbClient = ddbClient; + } + + /** + * Fetches the list of tables from DynamoDB via paginated ListTables calls + * + * @return the list of tables in DynamoDB + */ + public List listTables() + throws TimeoutException + { + List tables = new ArrayList<>(); + String nextToken = null; + do { + ListTablesRequest ddbRequest = new ListTablesRequest() + .withExclusiveStartTableName(nextToken); + ListTablesResult result = invoker.invoke(() -> ddbClient.listTables(ddbRequest)); + tables.addAll(result.getTableNames().stream().map(table -> new TableName(DEFAULT_SCHEMA, table)).collect(toImmutableList())); + nextToken = result.getLastEvaluatedTableName(); + } + while (nextToken != null); + return tables; + } + + /** + * Fetches table schema by first doing a Scan on the given table name, falling back to case insensitive + * resolution if the table isn't found. Delegates actual schema derivation to {@link + * DDBTableUtils#peekTableForSchema}. + * + * @param tableName the case insensitive table name + * @return the table's schema + */ + public Schema getTableSchema(String tableName) + throws TimeoutException + { + try { + return DDBTableUtils.peekTableForSchema(tableName, invoker, ddbClient); + } + catch (ResourceNotFoundException e) { + Optional caseInsensitiveMatch = tryCaseInsensitiveSearch(tableName); + if (caseInsensitiveMatch.isPresent()) { + return DDBTableUtils.peekTableForSchema(caseInsensitiveMatch.get(), invoker, ddbClient); + } + else { + throw e; + } + } + } + + /** + * Fetches table metadata by first doing a DescribeTable on the given table table, falling back to case + * insensitive resolution if the table isn't found. + * + * @param tableName the case insensitive table name + * @return the table's metadata + */ + public DynamoDBTable getTableMetadata(String tableName) + throws TimeoutException + { + try { + return DDBTableUtils.getTable(tableName, invoker, ddbClient); + } + catch (ResourceNotFoundException e) { + Optional caseInsensitiveMatch = tryCaseInsensitiveSearch(tableName); + if (caseInsensitiveMatch.isPresent()) { + return DDBTableUtils.getTable(caseInsensitiveMatch.get(), invoker, ddbClient); + } + else { + throw e; + } + } + } + + /* + Performs a case insensitive table search by listing the tables, mapping them to their lowercase transformation, + and then mapping the given tableName back to a unique table. To prevent ambiguity, an IllegalStateException is + thrown if multiple tables map to the given tableName. + */ + private Optional tryCaseInsensitiveSearch(String tableName) + throws TimeoutException + { + logger.info("Table {} not found. Falling back to case insensitive search.", tableName); + Multimap lowerCaseNameMapping = ArrayListMultimap.create(); + for (TableName nextTableName : listTables()) { + lowerCaseNameMapping.put(nextTableName.getTableName().toLowerCase(Locale.ENGLISH), nextTableName.getTableName()); + } + Collection mappedNames = lowerCaseNameMapping.get(tableName); + if (mappedNames.size() > 1) { + throw new IllegalStateException(String.format("Multiple tables resolved from case insensitive name %s: %s", tableName, mappedNames)); + } + else if (mappedNames.size() == 1) { + return Optional.of(mappedNames.iterator().next()); + } + else { + return Optional.empty(); + } + } +} diff --git a/athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/throttling/DynamoDBExceptionFilter.java b/athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/throttling/DynamoDBExceptionFilter.java new file mode 100644 index 0000000000..9f289f6aa3 --- /dev/null +++ b/athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/throttling/DynamoDBExceptionFilter.java @@ -0,0 +1,42 @@ +/*- + * #%L + * athena-dynamodb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.dynamodb.throttling; + +import com.amazonaws.athena.connector.lambda.ThrottlingInvoker; +import com.amazonaws.services.dynamodbv2.model.LimitExceededException; +import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughputExceededException; + +/** + * Used by {@link ThrottlingInvoker} to determine which DynamoDB exceptions are thrown for throttling. + */ +public class DynamoDBExceptionFilter + implements ThrottlingInvoker.ExceptionFilter +{ + public static final ThrottlingInvoker.ExceptionFilter EXCEPTION_FILTER = new DynamoDBExceptionFilter(); + + private DynamoDBExceptionFilter() {} + + @Override + public boolean isMatch(Exception ex) + { + return ex instanceof LimitExceededException + || ex instanceof ProvisionedThroughputExceededException; + } +} diff --git a/athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/util/DDBPredicateUtils.java b/athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/util/DDBPredicateUtils.java new file mode 100644 index 0000000000..16f1c3e1ad --- /dev/null +++ b/athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/util/DDBPredicateUtils.java @@ -0,0 +1,297 @@ +/*- + * #%L + * athena-dynamodb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.dynamodb.util; + +import com.amazonaws.athena.connector.lambda.domain.predicate.EquatableValueSet; +import com.amazonaws.athena.connector.lambda.domain.predicate.Range; +import com.amazonaws.athena.connector.lambda.domain.predicate.SortedRangeSet; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import com.amazonaws.athena.connectors.dynamodb.model.DynamoDBTable; +import com.amazonaws.services.dynamodbv2.document.ItemUtils; +import com.amazonaws.services.dynamodbv2.model.AttributeValue; +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableList; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Stream; + +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.collect.ImmutableList.toImmutableList; +import static com.google.common.collect.Iterables.getOnlyElement; + +/** + * Provides utility methods relating to predicate handling. + */ +public class DDBPredicateUtils +{ + private DDBPredicateUtils() {} + + private static final Joiner AND_JOINER = Joiner.on(" AND "); + private static final Joiner COMMA_JOINER = Joiner.on(","); + private static final Joiner OR_JOINER = Joiner.on(" OR "); + + /** + * Attempts to pick an optimal index (if any) from the given predicates. Returns the original table if + * one was not found. + * + * @param table the original table + * @param predicates the predicates + * @return the optimal index if found, otherwise the original table + */ + public static DynamoDBTable getBestIndexForPredicates(DynamoDBTable table, Map predicates) + { + Set columnNames = predicates.keySet(); + + ImmutableList.Builder hashKeyMatchesBuilder = ImmutableList.builder(); + // if the original table has a hash key matching a predicate, start with that + if (columnNames.contains(table.getHashKey())) { + hashKeyMatchesBuilder.add(table); + } + + // get indices with hash keys that match a predicate + table.getIndexes().stream() + .filter(index -> columnNames.contains(index.getHashKey()) && !getHashKeyAttributeValues(predicates.get(index.getHashKey())).isEmpty()) + .forEach(hashKeyMatchesBuilder::add); + List hashKeyMatches = hashKeyMatchesBuilder.build(); + + // if the original table has a range key matching a predicate, start with that + ImmutableList.Builder rangeKeyMatchesBuilder = ImmutableList.builder(); + if (table.getRangeKey().isPresent() && columnNames.contains(table.getRangeKey().get())) { + rangeKeyMatchesBuilder.add(table); + } + + // get indices with range keys that match a predicate + table.getIndexes().stream() + .filter(index -> index.getRangeKey().isPresent() && columnNames.contains(index.getRangeKey().get())) + .forEach(rangeKeyMatchesBuilder::add); + List rangeKeyMatches = rangeKeyMatchesBuilder.build(); + + // return first index where both hash and range key can be specified with predicates + for (DynamoDBTable index : hashKeyMatches) { + if (rangeKeyMatches.contains(index)) { + return index; + } + } + // else return the first index with a hash key predicate, or the original table if there are none + return hashKeyMatches.isEmpty() ? table : hashKeyMatches.get(0); + } + + /** + * Generates a list of distinct values from the given {@link ValueSet} or an empty list if not possible. + * + * @param valueSet the value set to generate from + * @return the list of distinct values + */ + public static List getHashKeyAttributeValues(ValueSet valueSet) + { + if (valueSet.isSingleValue()) { + return ImmutableList.of(valueSet.getSingleValue()); + } + else if (valueSet instanceof SortedRangeSet) { + List ranges = valueSet.getRanges().getOrderedRanges(); + ImmutableList.Builder attributeValues = ImmutableList.builder(); + for (Range range : ranges) { + if (range.isSingleValue()) { + attributeValues.add(range.getSingleValue()); + } + else { + // DDB Query can't handle non-equality conditions for the hash key + return ImmutableList.of(); + } + } + return attributeValues.build(); + } + else if (valueSet instanceof EquatableValueSet) { + EquatableValueSet equatableValueSet = (EquatableValueSet) valueSet; + if (equatableValueSet.isWhiteList()) { + ImmutableList.Builder values = ImmutableList.builder(); + for (int pos = 0; pos < equatableValueSet.getValueBlock().getRowCount(); pos++) { + values.add(equatableValueSet.getValue(pos)); + } + return values.build(); + } + } + + return ImmutableList.of(); + } + + /** + * Generates a simple alias for a column to satisfy filter expressions. + * + * @see + * https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.ExpressionAttributeNames.html + * @param columnName the input column name + * @return the aliased column name + */ + public static String aliasColumn(String columnName) + { + return "#" + columnName; + } + + /* + Adds a value to the value accumulator. + */ + private static void bindValue(Object value, List accumulator) + { + accumulator.add(ItemUtils.toAttributeValue(DDBTypeUtils.convertArrowTypeIfNecessary(value))); + } + + /* + Adds a value to the value accumulator and also returns an expression with given operands. + */ + private static String toPredicate(String columnName, String operator, Object value, List accumulator, String valueName) + { + bindValue(value, accumulator); + return columnName + " " + operator + " " + valueName; + } + + /** + * Generates a filter expression for a single column given a {@link ValueSet} predicate for that column. + * + * @param originalColumnName the column name + * @param predicate the associated predicate + * @param accumulator the value accumulator to add values to + * @param valueNameProducer the value name producer to generate value aliases with + * @return the generated filter expression + */ + public static String generateSingleColumnFilter(String originalColumnName, ValueSet predicate, List accumulator, + IncrementingValueNameProducer valueNameProducer) + { + String columnName = aliasColumn(originalColumnName); + + if (predicate.isNone()) { + return "(attribute_not_exists(" + columnName + ") OR " + toPredicate(columnName, "=", null, accumulator, valueNameProducer.getNext()) + ")"; + } + + if (predicate.isAll()) { + return "(attribute_exists(" + columnName + ") AND " + toPredicate(columnName, "<>", null, accumulator, valueNameProducer.getNext()) + ")"; + } + + List disjuncts = new ArrayList<>(); + List singleValues = new ArrayList<>(); + boolean isWhitelist = true; + if (predicate instanceof SortedRangeSet) { + for (Range range : predicate.getRanges().getOrderedRanges()) { + checkState(!range.isAll()); // Already checked + if (range.isSingleValue()) { + singleValues.add(range.getLow().getValue()); + } + else { + List rangeConjuncts = new ArrayList<>(); + if (!range.getLow().isLowerUnbounded()) { + switch (range.getLow().getBound()) { + case ABOVE: + rangeConjuncts.add(toPredicate(columnName, ">", range.getLow().getValue(), accumulator, valueNameProducer.getNext())); + break; + case EXACTLY: + rangeConjuncts.add(toPredicate(columnName, ">=", range.getLow().getValue(), accumulator, valueNameProducer.getNext())); + break; + case BELOW: + throw new IllegalArgumentException("Low marker should never use BELOW bound"); + default: + throw new AssertionError("Unhandled lower bound: " + range.getLow().getBound()); + } + } + if (!range.getHigh().isUpperUnbounded()) { + switch (range.getHigh().getBound()) { + case ABOVE: + throw new IllegalArgumentException("High marker should never use ABOVE bound"); + case EXACTLY: + rangeConjuncts.add(toPredicate(columnName, "<=", range.getHigh().getValue(), accumulator, valueNameProducer.getNext())); + break; + case BELOW: + rangeConjuncts.add(toPredicate(columnName, "<", range.getHigh().getValue(), accumulator, valueNameProducer.getNext())); + break; + default: + throw new AssertionError("Unhandled upper bound: " + range.getHigh().getBound()); + } + } + // If rangeConjuncts is null, then the range was ALL, which should already have been checked for + checkState(!rangeConjuncts.isEmpty()); + disjuncts.add("(" + AND_JOINER.join(rangeConjuncts) + ")"); + } + } + } + else { + EquatableValueSet equatablePredicate = (EquatableValueSet) predicate; + isWhitelist = equatablePredicate.isWhiteList(); + long valueCount = equatablePredicate.getValueBlock().getRowCount(); + for (int i = 0; i < valueCount; i++) { + singleValues.add(equatablePredicate.getValue(i)); + } + } + + // Add back all of the possible single values either as an equality or an IN predicate + if (singleValues.size() == 1) { + disjuncts.add(toPredicate(columnName, isWhitelist ? "=" : "<>", getOnlyElement(singleValues), accumulator, valueNameProducer.getNext())); + } + else if (singleValues.size() > 1) { + for (Object value : singleValues) { + bindValue(value, accumulator); + } + String values = COMMA_JOINER.join(Stream.generate(valueNameProducer::getNext).limit(singleValues.size()).collect(toImmutableList())); + disjuncts.add((isWhitelist ? "" : "NOT ") + columnName + " IN (" + values + ")"); + } + + // at this point we should have some disjuncts + checkState(!disjuncts.isEmpty()); + + // add nullability disjuncts + if (predicate.isNullAllowed()) { + disjuncts.add("attribute_not_exists(" + columnName + ") OR " + toPredicate(columnName, "=", null, accumulator, valueNameProducer.getNext())); + } + + // DDB doesn't like redundant parentheses + if (disjuncts.size() == 1) { + return disjuncts.get(0); + } + + return "(" + OR_JOINER.join(disjuncts) + ")"; + } + + /** + * Generates a combined filter expression for the given predicates. + * + * @param columnsToIgnore the columns to not generate filters for + * @param predicates the map of columns to predicates + * @param accumulator the value accumulator to add values to + * @param valueNameProducer the value name producer to generate value aliases with + * @return the combined filter expression + */ + public static String generateFilterExpression(Set columnsToIgnore, Map predicates, List accumulator, + IncrementingValueNameProducer valueNameProducer) + { + ImmutableList.Builder builder = ImmutableList.builder(); + for (Map.Entry predicate : predicates.entrySet()) { + String columnName = predicate.getKey(); + if (!columnsToIgnore.contains(columnName)) { + builder.add(generateSingleColumnFilter(columnName, predicate.getValue(), accumulator, valueNameProducer)); + } + } + ImmutableList filters = builder.build(); + if (!filters.isEmpty()) { + return AND_JOINER.join(filters); + } + return null; + } +} diff --git a/athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/util/DDBTableUtils.java b/athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/util/DDBTableUtils.java new file mode 100644 index 0000000000..25700e5e14 --- /dev/null +++ b/athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/util/DDBTableUtils.java @@ -0,0 +1,216 @@ +/*- + * #%L + * athena-dynamodb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.dynamodb.util; + +import com.amazonaws.athena.connector.lambda.ThrottlingInvoker; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connectors.dynamodb.model.DynamoDBTable; +import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; +import com.amazonaws.services.dynamodbv2.document.ItemUtils; +import com.amazonaws.services.dynamodbv2.model.AttributeDefinition; +import com.amazonaws.services.dynamodbv2.model.AttributeValue; +import com.amazonaws.services.dynamodbv2.model.DescribeTableRequest; +import com.amazonaws.services.dynamodbv2.model.GlobalSecondaryIndexDescription; +import com.amazonaws.services.dynamodbv2.model.KeySchemaElement; +import com.amazonaws.services.dynamodbv2.model.KeyType; +import com.amazonaws.services.dynamodbv2.model.LocalSecondaryIndexDescription; +import com.amazonaws.services.dynamodbv2.model.ScanRequest; +import com.amazonaws.services.dynamodbv2.model.ScanResult; +import com.amazonaws.services.dynamodbv2.model.TableDescription; +import com.google.common.collect.ImmutableList; +import org.apache.arrow.vector.types.pojo.Schema; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.TimeoutException; + +/** + * Provides utility methods relating to table handling. + */ +public final class DDBTableUtils +{ + private static final Logger logger = LoggerFactory.getLogger(DDBTableUtils.class); + + // for scan segmentation calculation + private static final long PSUEDO_CAPACITY_FOR_ON_DEMAND = 40_000; + private static final int MAX_SCAN_SEGMENTS = 1000000; + private static final int MIN_SCAN_SEGMENTS = 1; + private static final long MAX_BYTES_PER_SEGMENT = 1024L * 1024L * 1024L; + private static final double MIN_IO_PER_SEGMENT = 100.0; + private static final int SCHEMA_INFERENCE_NUM_RECORDS = 4; + + private DDBTableUtils() {} + + /** + * Fetches metadata for a DynamoDB table + * + * @param tableName the (case sensitive) table name + * @param invoker the ThrottlingInvoker to call DDB with + * @param ddbClient the DDB client to use + * @return the table metadata + */ + public static DynamoDBTable getTable(String tableName, ThrottlingInvoker invoker, AmazonDynamoDB ddbClient) + throws TimeoutException + { + DescribeTableRequest request = new DescribeTableRequest().withTableName(tableName); + TableDescription table = invoker.invoke(() -> ddbClient.describeTable(request).getTable()); + + KeyNames keys = getKeys(table.getKeySchema()); + + // get data statistics + long approxTableSizeInBytes = table.getTableSizeBytes(); + long approxItemCount = table.getItemCount(); + final long provisionedReadCapacity = table.getProvisionedThroughput() != null ? table.getProvisionedThroughput().getReadCapacityUnits() : PSUEDO_CAPACITY_FOR_ON_DEMAND; + + // get secondary indexes + List localSecondaryIndexes = table.getLocalSecondaryIndexes() != null ? table.getLocalSecondaryIndexes() : ImmutableList.of(); + List globalSecondaryIndexes = table.getGlobalSecondaryIndexes() != null ? table.getGlobalSecondaryIndexes() : ImmutableList.of(); + ImmutableList.Builder indices = ImmutableList.builder(); + localSecondaryIndexes.forEach(i -> { + KeyNames indexKeys = getKeys(i.getKeySchema()); + indices.add(new DynamoDBTable(i.getIndexName(), indexKeys.getHashKey(), indexKeys.getRangeKey(), table.getAttributeDefinitions(), ImmutableList.of(), i.getIndexSizeBytes(), i.getItemCount(), + provisionedReadCapacity)); + }); + globalSecondaryIndexes.forEach(i -> { + KeyNames indexKeys = getKeys(i.getKeySchema()); + indices.add(new DynamoDBTable(i.getIndexName(), indexKeys.getHashKey(), indexKeys.getRangeKey(), table.getAttributeDefinitions(), ImmutableList.of(), i.getIndexSizeBytes(), i.getItemCount(), + i.getProvisionedThroughput() != null ? i.getProvisionedThroughput().getReadCapacityUnits() : PSUEDO_CAPACITY_FOR_ON_DEMAND)); + }); + + return new DynamoDBTable(tableName, keys.getHashKey(), keys.getRangeKey(), table.getAttributeDefinitions(), indices.build(), approxTableSizeInBytes, approxItemCount, provisionedReadCapacity); + } + + /* + Parses the key attributes from the given list of KeySchemaElements + */ + private static KeyNames getKeys(List keys) + { + String hashKey = null; + String rangeKey = null; + for (KeySchemaElement key : keys) { + if (key.getKeyType().equals(KeyType.HASH.toString())) { + hashKey = key.getAttributeName(); + } + else if (key.getKeyType().equals(KeyType.RANGE.toString())) { + rangeKey = key.getAttributeName(); + } + } + return new KeyNames(hashKey, rangeKey); + } + + /** + * Derives an Arrow {@link Schema} for the given table by performing a small table scan and mapping the returned + * attribute values' types to Arrow types. If the table is empty, only attributes found in the table's metadata + * are added to the return schema. + * + * @param tableName the table to derive a schema for + * @param invoker the ThrottlingInvoker to call DDB with + * @param ddbClient the DDB client to use + * @return the table's derived schema + */ + public static Schema peekTableForSchema(String tableName, ThrottlingInvoker invoker, AmazonDynamoDB ddbClient) + throws TimeoutException + { + ScanRequest scanRequest = new ScanRequest().withTableName(tableName).withLimit(SCHEMA_INFERENCE_NUM_RECORDS); + ScanResult scanResult = invoker.invoke(() -> ddbClient.scan(scanRequest)); + List> items = scanResult.getItems(); + Set discoveredColumns = new HashSet<>(); + SchemaBuilder schemaBuilder = new SchemaBuilder(); + if (!items.isEmpty()) { + for (Map item : items) { + for (Map.Entry column : item.entrySet()) { + if (!discoveredColumns.contains(column.getKey()) && !Boolean.TRUE.equals(column.getValue().getNULL())) { + schemaBuilder.addField(DDBTypeUtils.getArrowField(column.getKey(), ItemUtils.toSimpleValue(column.getValue()))); + discoveredColumns.add(column.getKey()); + } + } + } + } + else { + // there's no items, so use any attributes defined in the table metadata + DynamoDBTable table = getTable(tableName, invoker, ddbClient); + for (AttributeDefinition attributeDefinition : table.getKnownAttributeDefinitions()) { + schemaBuilder.addField(DDBTypeUtils.getArrowFieldFromDDBType(attributeDefinition.getAttributeName(), attributeDefinition.getAttributeType())); + } + } + return schemaBuilder.build(); + } + + /** + * This hueristic determines an optimal segment count to perform Parallel Scans with using the table's capacity + * and size. + * + * @see + * https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Scan.html#Scan.ParallelScan + * @param tableNormalizedReadThroughput the provisioned read capacity for the table + * @param currentTableSizeBytes the table's approximate size in bytes + * @return an optimal segment count + */ + public static int getNumSegments(long tableNormalizedReadThroughput, long currentTableSizeBytes) + { + // Segments for size + int numSegmentsForSize = (int) (currentTableSizeBytes / MAX_BYTES_PER_SEGMENT); + logger.debug("Would use {} segments for size", numSegmentsForSize); + + // Segments for total throughput + int numSegmentsForThroughput = (int) (tableNormalizedReadThroughput / MIN_IO_PER_SEGMENT); + logger.debug("Would use {} segments for throughput", numSegmentsForThroughput); + + // Take the larger + int numSegments = Math.max(numSegmentsForSize, numSegmentsForThroughput); + + // Fit to bounds + numSegments = Math.min(numSegments, MAX_SCAN_SEGMENTS); + numSegments = Math.max(numSegments, MIN_SCAN_SEGMENTS); + + logger.debug("Using computed number of segments: {}", numSegments); + return numSegments; + } + + /* + Simple convenient holder for key data + */ + private static class KeyNames + { + private String hashKey; + private String rangeKey; + + private KeyNames(String hashKey, String rangeKey) + { + this.hashKey = hashKey; + this.rangeKey = rangeKey; + } + + private String getHashKey() + { + return hashKey; + } + + private Optional getRangeKey() + { + return Optional.ofNullable(rangeKey); + } + } +} diff --git a/athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/util/DDBTypeUtils.java b/athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/util/DDBTypeUtils.java new file mode 100644 index 0000000000..34f8eb582c --- /dev/null +++ b/athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/util/DDBTypeUtils.java @@ -0,0 +1,161 @@ +/*- + * #%L + * athena-dynamodb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.dynamodb.util; + +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.arrow.vector.types.pojo.FieldType; +import org.apache.arrow.vector.util.Text; +import org.joda.time.DateTimeZone; +import org.joda.time.LocalDateTime; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Provides utility methods relating to type handling. + */ +public final class DDBTypeUtils +{ + // DDB attribute "types" + private static final String STRING = "S"; + private static final String NUMBER = "N"; + private static final String BOOLEAN = "BOOL"; + private static final String BINARY = "B"; + private static final String STRING_SET = "SS"; + private static final String NUMBER_SET = "NS"; + private static final String BINARY_SET = "BS"; + private static final String LIST = "L"; + private static final String MAP = "M"; + + private DDBTypeUtils() {} + + /** + * Converts a given field's Java type to a corresponding Arrow type. + * + * @param key the name of the field + * @param value the valie of the field + * @return the converted Arrow field + */ + public static Field getArrowField(String key, Object value) + { + if (value instanceof String) { + return new Field(key, FieldType.nullable(Types.MinorType.VARCHAR.getType()), null); + } + else if (value instanceof byte[]) { + return new Field(key, FieldType.nullable(Types.MinorType.VARBINARY.getType()), null); + } + else if (value instanceof Boolean) { + return new Field(key, FieldType.nullable(Types.MinorType.BIT.getType()), null); + } + else if (value instanceof BigDecimal) { + return new Field(key, FieldType.nullable(new ArrowType.Decimal(38, 9)), null); + } + else if (value instanceof List || value instanceof Set) { + Field child; + if (((Collection) value).isEmpty()) { + try { + Object subVal = ((Collection) value).getClass() + .getTypeParameters()[0].getGenericDeclaration().newInstance(); + child = getArrowField("", subVal); + } + catch (IllegalAccessException | InstantiationException ex) { + throw new RuntimeException(ex); + } + } + else { + child = getArrowField("", ((Collection) value).iterator().next()); + } + return new Field(key, FieldType.nullable(Types.MinorType.LIST.getType()), + Collections.singletonList(child)); + } + else if (value instanceof Map) { + List children = new ArrayList<>(); + Map doc = (Map) value; + for (String childKey : doc.keySet()) { + Object childVal = doc.get(childKey); + Field child = getArrowField(childKey, childVal); + children.add(child); + } + return new Field(key, FieldType.nullable(Types.MinorType.STRUCT.getType()), children); + } + + String className = value.getClass() == null ? "null" : value.getClass().getName(); + throw new RuntimeException("Unknown type[" + className + "] for field[" + key + "]"); + } + + /** + * Converts certain Arrow POJOs to Java POJOs to make downstream conversion easier. + * + * @param object the input object + * @return the converted-to object if convertible, otherwise the original object + */ + public static Object convertArrowTypeIfNecessary(Object object) + { + if (object instanceof Text) { + return object.toString(); + } + else if (object instanceof LocalDateTime) { + return ((LocalDateTime) object).toDateTime(DateTimeZone.UTC).getMillis(); + } + return object; + } + + /** + * Converts from DynamoDB Attribute Type to Arrow type. + * @param attributeName the DDB Attribute name + * @param attributeType the DDB Attribute type + * @return the converted-to Arrow Field + */ + public static Field getArrowFieldFromDDBType(String attributeName, String attributeType) + { + switch (attributeType) { + case STRING: + return new Field(attributeName, FieldType.nullable(Types.MinorType.VARCHAR.getType()), null); + case NUMBER: + return new Field(attributeName, FieldType.nullable(new ArrowType.Decimal(38, 9)), null); + case BOOLEAN: + return new Field(attributeName, FieldType.nullable(Types.MinorType.BIT.getType()), null); + case BINARY: + return new Field(attributeName, FieldType.nullable(Types.MinorType.VARBINARY.getType()), null); + case STRING_SET: + return new Field(attributeName, FieldType.nullable(Types.MinorType.LIST.getType()), + Collections.singletonList(new Field("", FieldType.nullable(Types.MinorType.VARCHAR.getType()), null))); + case NUMBER_SET: + return new Field(attributeName, FieldType.nullable(Types.MinorType.LIST.getType()), + Collections.singletonList(new Field("", FieldType.nullable(new ArrowType.Decimal(38, 9)), null))); + case BINARY_SET: + return new Field(attributeName, FieldType.nullable(Types.MinorType.LIST.getType()), + Collections.singletonList(new Field("", FieldType.nullable(Types.MinorType.VARBINARY.getType()), null))); + case LIST: + return new Field(attributeName, FieldType.nullable(Types.MinorType.LIST.getType()), null); + case MAP: + return new Field(attributeName, FieldType.nullable(Types.MinorType.STRUCT.getType()), null); + default: + throw new RuntimeException("Unknown type[" + attributeType + "] for field[" + attributeName + "]"); + } + } +} diff --git a/athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/util/IncrementingValueNameProducer.java b/athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/util/IncrementingValueNameProducer.java new file mode 100644 index 0000000000..bfda748837 --- /dev/null +++ b/athena-dynamodb/src/main/java/com/amazonaws/athena/connectors/dynamodb/util/IncrementingValueNameProducer.java @@ -0,0 +1,41 @@ +/*- + * #%L + * athena-dynamodb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.dynamodb.util; + +/** + * A simple, repeatable name producer used to alias values in DynamoDB filter expressions. + * + * @see + * https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.ExpressionAttributeValues.html + */ +public class IncrementingValueNameProducer +{ + private int i = 0; + + /** + * Returns the next alias. + * + * @return the next alias + */ + public String getNext() + { + return ":v" + i++; + } +} diff --git a/athena-dynamodb/src/test/java/com/amazonaws/athena/connectors/dynamodb/DynamoDBMetadataHandlerTest.java b/athena-dynamodb/src/test/java/com/amazonaws/athena/connectors/dynamodb/DynamoDBMetadataHandlerTest.java new file mode 100644 index 0000000000..211f00db30 --- /dev/null +++ b/athena-dynamodb/src/test/java/com/amazonaws/athena/connectors/dynamodb/DynamoDBMetadataHandlerTest.java @@ -0,0 +1,435 @@ +/*- + * #%L + * athena-dynamodb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.dynamodb; + +import com.amazonaws.AmazonServiceException; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.domain.predicate.EquatableValueSet; +import com.amazonaws.athena.connector.lambda.domain.predicate.Range; +import com.amazonaws.athena.connector.lambda.domain.predicate.SortedRangeSet; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesResponse; +import com.amazonaws.athena.connector.lambda.metadata.MetadataRequestType; +import com.amazonaws.athena.connector.lambda.metadata.MetadataResponse; +import com.amazonaws.athena.connector.lambda.security.LocalKeyFactory; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.dynamodbv2.document.ItemUtils; +import com.amazonaws.services.dynamodbv2.model.AttributeValue; +import com.amazonaws.services.glue.AWSGlue; +import com.amazonaws.services.glue.model.GetTablesResult; +import com.amazonaws.services.glue.model.StorageDescriptor; +import com.amazonaws.services.glue.model.Table; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.amazonaws.util.json.Jackson; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.joda.time.Days; +import org.joda.time.LocalDateTime; +import org.joda.time.MutableDateTime; +import org.junit.After; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.EXPRESSION_NAMES_METADATA; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.EXPRESSION_VALUES_METADATA; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.HASH_KEY_NAME_METADATA; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.INDEX_METADATA; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.NON_KEY_FILTER_METADATA; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.PARTITION_TYPE_METADATA; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.QUERY_PARTITION_TYPE; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.RANGE_KEY_FILTER_METADATA; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.RANGE_KEY_NAME_METADATA; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.SCAN_PARTITION_TYPE; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.SEGMENT_COUNT_METADATA; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.SEGMENT_ID_PROPERTY; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.DEFAULT_SCHEMA; +import static com.amazonaws.athena.connectors.dynamodb.DynamoDBMetadataHandler.MAX_SPLITS_PER_REQUEST; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.when; + +/** + * Glue logic is tested by GlueMetadataHandlerTest in SDK + */ +@RunWith(MockitoJUnitRunner.class) +public class DynamoDBMetadataHandlerTest + extends TestBase +{ + private static final Logger logger = LoggerFactory.getLogger(DynamoDBMetadataHandlerTest.class); + + @Mock + private AWSGlue glueClient; + + @Mock + private AWSSecretsManager secretsManager; + + @Mock + private AmazonAthena athena; + + private DynamoDBMetadataHandler handler; + + private BlockAllocator allocator; + + @Before + public void setup() + { + allocator = new BlockAllocatorImpl(); + handler = new DynamoDBMetadataHandler(new LocalKeyFactory(), secretsManager, athena, "spillBucket", "spillPrefix", ddbClient, glueClient); + } + + @After + public void tearDown() + { + allocator.close(); + } + + @Test + public void doListSchemaNamesDynamo() + throws Exception + { + logger.info("doListSchemaNamesDynamo: enter"); + + when(glueClient.getDatabases(any())).thenThrow(new AmazonServiceException("")); + + ListSchemasRequest req = new ListSchemasRequest(TEST_IDENTITY, TEST_QUERY_ID, TEST_CATALOG_NAME); + ListSchemasResponse res = handler.doListSchemaNames(allocator, req); + + logger.info("doListSchemas - {}", res.getSchemas()); + + assertThat(new ArrayList<>(res.getSchemas()), equalTo(Collections.singletonList(DEFAULT_SCHEMA))); + + logger.info("doListSchemaNamesDynamo: exit"); + } + + @Test + public void doListTablesGlueAndDynamo() + throws Exception + { + logger.info("doListTablesGlueAndDynamo: enter"); + + List tableNames = new ArrayList<>(); + tableNames.add("table1"); + tableNames.add("table2"); + tableNames.add("table3"); + + GetTablesResult mockResult = new GetTablesResult(); + List

U?g!c{**Od$PIM;(pWoEoBjc!%CUlfF|p$4WIP&lrVPG*(sf$=phSE zMv?}C`8j){gg_R-C1_FP&J>hP_o}Bx)R@S?D7Xq`%Cw(-CEX#oNzu<5f%PS(3(a6K zeJT$k&S;$PDiTM<8?372VfeXNp`D4v?p?nrW{=6;Oeqih^CsD5Wt-1&&$!!E9Cx;% zXAGt#-Cr;BXp+)25jPcrJ&>yRFGbz4C*BYaC<$E?MyTD1VMKLj3b|3(D1Xe!$1;lu z2YWfxlGpVZ;ItL*W3Twz&da7rM6H@1t5yD>Zn!8ak{u^#* z5cWd68#*|%Fdz%CT)xqC=@9Y6O@W50{yt%hSL_qm&GZQeYWl=ScIIZZ1WJBUoX(JI zp<{GD4+rlgtf%rFq5RgYN5(#8{@VVn-fH2lJX)Q&otL>5h^bN??&M!HqXw{^klJ@; za=7|D*n@%0D^{P|jL4YpqGNYQGWZp4^8#WH+jeQA*RxkT_?Q$Wf$R-sIoYG6_V^Lw?ZH)RB6rT_)o=UV zZ<19(P<*^1hzGKB2Idas9pTBOLFyqtnO`TS&fRw{=@W`s0jRzv?!4A;NOD-Sa|uhz zz146?Vpn!@fo8jCmK@Llk`^O9<1i6^V0IOR7ovIf%+9iXQIt<)Ne$z(P#K2a*b$02 zT!XTS2SjzP@l8q9uJP)RVbM%Nu;1&yHo6x~-))+nTwNR=dTg{f2DLR*C6EOO5=^E> zMgKdO*y$UjL7Pl7R#l#Te;HQ{OT45i$}GWMiy0w?dG0fIgj*E}&M{J*e373&;WdqyDw|1Majl4LK8kEIQQprM zKpJ8?Ymc58$RAG{3A(GdM*qc3lx9SeVP?gf5j9YQb|ecI7T*J;P0k(vsRi2?1n^Lj%bOM+-d%_MLv}hW}GYE8r zpCti4hyZzy=sQ!FdyA_`=>5gW05-cD7bqIRcIo|9!p;_zhU`63RG`{_IbKr2x9$bp zJO*6zIopt02M|!VTEXb+p*NCoyW!+SqX}ao`Sl8zCsXD$RLMk!N5p$v4EdY2Y6xFj z&U5bV1Z6cPhVdL>*mLd@@5vj9Mw}wtaX4JrnUhodh9^g4r|*T^Ibu7kh(+cB%XvLN z!V0~Ph$%bt^|vscfw-;Xg}_RW#*isfX484jTF7pg!MV8BFQ|z*i|Ryr&%%b@`_A4%*#r8h z$ma&;A5`|hANubHE#O|up88z}|NFdt0Iatp2DRo*7a`nu1oNfGRzPdX+Dd;QQp)sVs zx-!{5;dxlMhUs-nu0jY1yU(0Q%7FvjiI|l9M#RD~TV^!hJPkxx`&6qEGN+_1R>`AD zLb&7z;1>xe8f$3E6n0*i=9HHgwmMrQ{v9z$g-B>_UHjdg&u4xr_9z(rEVCfum}~8x zDV1LkVSt2H9{*XKzrhPeq`tZ$5u~p=qg&jm%}O=xG}_04!K6>+bhjvZ!c0*kCbcCg zp}`-e{c=GucY?j_!+G=<4nk>VoR6Z;KVqTH;wwiUf^?3ME6ypgP;U~zI`a7k{JvEf zmH6Dp?)ajx>N7NJ?k^?nmT{Oa_^6$KaLSKPe^HKV|?X4 z-znx2;RDk9(nKBHR*vOq=%$5C{9^o+o95VUn~b(QJEDxhN^{(Iw;&jKvT0%AY4QoZ zdEnRYepkUQNL=x-HjcY60Zengaz3^~UnNh}ydRSk0rgRPAo$1weT%UHQGsI?!$c#u zu!v(z|FE9fK_`9F6ak;<>>vDY=QBHoHF=q)N`M7X4^8L<2o_)=Gk)z1A;5*e~mQBg*On5{^`|?z8tnE; zvwXCpbK=fBY}HJatnl0uVIwt`E0Wl~M_WDn=)fdr#-y_E|Vp#iE%?H!0}aoU40 z&Dn~u{t#`U6M1{ZbZ*$yuM^x&&)i?#rfyHIHsPCXXQw+G(L^zM-&B&8Y)H&?r|0oSj6Fu}mXm24236RUP)VRYcl2 z(p~?81X5FVx~hLAzP{TXWxBCb$C%9s8jNLU(Fi!5cek=_Qj5seKJbZh)(rh?R>YtV+eY_BNeFiWy?7v&CR@?* zOTW*&Lnde9YHz{tHGXWj+w>#Nm(lWW&mc0Ajhp))L{V2`(7Rr9XVILbNsu-g1Wc6S zj*9DTn4~y|`fE8&TMgLNh!o=s?`!w*Gfn3D;HrV(B6n;ZgZspQ9W4zx*3UZi9F{W} zC5wz1=HD*Y>2S2KN6(GuVTGI7NP@g`kl7Rj3rHO zMvm!>%9t*HXQ8mER>L(}GF4M&ucXB72ur`qEs^V<=wf1xXU1l?3tGb_riO5q&vAq4 zef$;bI?CvDcc^6N4w5c#5qzX=Tp*Zw$9tB%vN2nJ4yz)Q$?S-isGImI6*3CkuFi$Q zGQsq5JZ82g6r!DlCAe$5?DN|>#)6XGq1}aD9uPN{iG1m$RV}o?CJ9&AWBgKIUXhGX z%T|6t6%*dmirA{^wBpz87`KWjwCzm!=d_qa-DzOKv_piNDw4MlbWGKU3UaokS|M5> zh$mcZ2HHD+M?kId^Yw)^M+6eXJ8gE?-k{lJ=CJ9f<0oF&49@XWAz}R{^_JI_B|kNg zXQR7#IVOVY9u0KvNKVL2pbFF6x<*nt9t+@kT-({5XR0&{kC}?M^uygBZh*vvF$oAj zHB+Qm?Qv9#+ZH>2dpRgnIidj@fAuIx$}iQc(OY#&dl-CTWJkSE1{JG28hpt;V|$jp z4Lx#>ocG&+Pn+B`u0&ItA(<$#@bu|Fw&>GgpcEMj-;XIyeq3=|8rYdxWcFsYyQ2rw zNXT)Iyw&&RVqyf4iaYw#S)3#||GR7NW1O6$*8R9c@>YWjv8(&Hqcl(1v3DU}@XT30 zL4zgIM;T|UINqmUDad%%L-#WA!?^$P73Tg4?jcGaoZ@G55dzAbk9aLvj?E#7OAvY< zwqETV8M75>ogKoti11%4cUQQ7bmWzFMeP-l(7VCTpK@B2G?Qe5N@Hle1~wDvw$6R| zJ=O*pe|aP@$BsMKQzU*OhLjtVha1bSNa&MffY`b9vj^GpNgvuqP4yQYoETcLM$}(4 zJXzt6e;ivieN=x@Rq{-t&J-TS?8MnlrK6FQygdxNnry{GKQ|OMiUC2Yx-$SF|K#vs z@uLaR>56Bss2y*Nd`xYHwLo>}1$W1<_}>3dY*X6*$=)8Xm(>Ug!X;i~!fP4CIFoBa zFC;AC_eki|8@f{_=y&ih`zfhg_Y!*qWvoq6L$(;4;Tp3@Qk8g3*P zp%)ysx@x1fo;}E#@4ZmvXoXH>PubNgtnYPAB4<_P#Oj8biR9ty!-=inVe`QB+i6L- zHfIST-1?SzHy%cRPVDc`F#3~wgz3;Qhz;9b5q#vv zaX1xE7M^$(IU?pdggr+%wH2By|9muwO$zCWWQzLm1bi`;_*4Kt=V z>RYW4a21Pvc8O~HDT*L=i!`~Qlg;k$R((;ZzpO=$f}5?!L~OV_XP`YL+TXz7BeTa< zEkMsW8B6YWy;)Q9nl+5ww5Ex{W;od@d(W>7;WwQe}{lfBRF zdh_+@?1g1~D^Rn~P{L&kg@|x~)~=NbjTJ7*LG#vCwbOZZ@gOkt>v^ zu7v9XCzo5P(QNpl-#Gbf0eZ)p4OH&~^G5Wat^Znlt5g|ALvgLKjEAIdaxcDYKVjBT z!_#IaM{>Qq6Zq67B<*X(^!eC>8MNiG3`=)wQ}T{e9JezEvlAzl!~5%z@pqW~FNs6| zcmlp-m1!5j-w-#{p~v`d0~@dU8EpJ5B**m1W&G|@yahhAB~^ z8S>X^|3Mn4stkQ}6!dhr-e9FWXsIMP>kQ3du@XpX=+|f2u@+o*ThN zcgLYbKmqz}j6!*#KOVVli$PP((BRf`vI>ALUUEM@n1j=J6naIGE86liMNhLa6SZ@+ zdHW7^?rvf!+no3eW)pq+-0i^mB`(>L`hdA_?UwU6B4oG^Lde#LVj}4<34h-+VGDN^ zQFZeH*@!*rw!_m&?gIz!1_0;UdZxiLK-1aXD^ggwoJ?-! zfKH)^(HPPgSvc);ZFVG;R9b~KvDDh}kX&DW*H54-258DQ zx%NX6!Q5?R=gpiae{+WIP!#CXA&3d4sVEiHF5m>zWEenEB(5Tn4BNEm;_2NJ#+w-} zB#ltCK}XZf^|Zxm%$qie6g!v#_DPT2)9A~}WnUDsi-;xFV~G5Gg^@tVun^A zhToZff!R)}2Kpivht9J{=ER^>QgjS7DG(4??-#x!nUP^Zpu#*yF`>ooU4k!L06D*3 z#IB*O%P#{ILw>&=%OEJsEU5OHfLI0&-VBFhn85D=S6)ee1GAVqfZ{F8MhwqGiA9~4 z(b>4}g$B+v>g{QoE5Ik@yk2XfYTR$}-lY7_R($yE0q>1Uz&))0{r{uv-Q%M!?*0EH zo4|7O6A&~iO01Cv@sbMK#KpQBHn1xj2!e_?K!ZvZEoy?Xa*1wY2_F`FTKhe{czWzP zwpwj#dyb`uRuhoBB%rm@dMRr4OpF&4D;Jggp0AnDCIR)2pO1&+vorIV&wJkUp7*?O z^PUI4tbU&r+PN}3Q*h;%)xRY56W*@zYfn2GqlQ*)!tPUcpjkVXy)PXtwGy4@Ldb%s zoQt^KyWlVC7fsm0`#t(gM^JS)eoy90gfwZ@!k4w3VsFUm&mvN~_^|x)L3KB2KFTGU zZNh}&?*f`BDI2CXtuRxQYpP~QsGnB2e9j>A>vt7$Ft0wJhZPmjtraraca=l0?N-v*Zc{8XYUf zsk&VH(=KNZSfJO>mdl_9ft3?t+u+?5mz5&4{plxSN101&=Lh6v0AOiE+QuVke0jIb= z0lNk_SV4HGNo0VGL$~g99+Xn5Vn+T~a~Fry?Tnvh2WPgtbnQ!5*6bdKmYef7MYPO>C)2ew$~ug7*TyS;CDB|PB( zFqIS0xWHnK1*?g;kc~JfCt9t;{OskDTL1pDtz@-U5vi0)bEO{7qrqT@pRvP zJAA_WiIy=PpVq&_+?2QU;}ZiSU3CT4Qz^KkBEv+MB-tb_+!p_V3;m8F`3e6f@3r>l zej8&0bGv3W-Z=;|NAQIH=fWcZ2Xw8vW*i5<;Is^F>upFSph-X|lOezyCG#}sU*h;xI;YC@~mbY#*H*)ET zz<9y9Rc&uE+h*o*mK85*-7%`$HizlozeX#X3yb6XS(h!$kfkq{u(Q(53GRf3$&Q3eI45lfysn~p*SN$3KQBsy(mzSz-HD?lC z$B-7Q5dghVC4**sQxPc=qCF6C3x^LENQBd`3&a5h^~XU8fY@7-y;&XcfX?mdCUeXy zw(nwFWX9XXjZ~U%%@*RYk-!0Um7gO8N63rWHo#-xC$sxSF?(rw4mrR41E%BjPrG5q z!$lkHbF7}1V3>JkA|!ZtS;(B#r;J8m^(AuxWsT2R|Jae86d2vJ+4zi@15UTYd+TR( zf4kP7Bbp=W*7;I7Az2)|SggOgc6X}won&n%hMY{)&>6ZIJC!+ep%}Avr|+g}@7ZM; z5u86WBe&XnY(sVES$z2eQ=tv!M=4P?Rp!&T99^@7_=%z9`9tzvy2_8P%=SkNL*e3+ z;>4f%&vI->{OvL+4wG?R#QE?8pMav>LOPTe>ye0NHyOh|rjlqe= z`~!@@`h@>(X^llWfICy|YKYkj5?D#BNG-Kjh*yjI9Z1MroFqR%?yRAlk6DAb6oa)m<^klMU=t*KSl?Y6CkJE&E#q zw(CmDciw`Q$N>ut7@q;N1)|vK@lLgxT?yeOd#{{}LRd?5`;i#>J?(^<{C_$O6<+a0 z3<*DY?uk=E-GSa}ATxe3QI^A09A96}(Mb_Ohbdyo$gul;p{W*P2*mepf)&F4wDPR5 z9vi#ON$wBOFzIhggr-Gl(fMZ!;;n^hr#>X4HAoz~qWNX7`9CQMKGkl1%dNL@e=1!H zPf+VZSWJBE=2U1>)I6*OG!^y4y7LxckEI_@eb#RAccns@D^-qe_-k;AQtvbf=>zto zIulQS`nZMofdCSinHx`>JhQ4T6^%FOso1>8YDc_5Rtr+Pq@S%J3ZlpE$^^w}c!VE= zE{~U5VMR-<2pmG-W0@PJKKy63c4{en^#h$V{$q$drLzVK?^>M11KBdlU$usXZEjWL zmWkyOW}f%&Q5j?`0&ajV@IJJ&m70+Y92s*p0;I55i0d%@No64kb(s3?FvGDE$p^*% zaFGu4*(~?Y?e-lZ(tGNtjH}V2Xyy_1kzr2Tja*##qXtRA9VPv z-B9I!93T5H?V;R@K^hT_>Er6!Lkj-bq%z*3=wB&LUkTe{0G!4Za#h)8D>D;@#zOmJ zp+m9I0eM7L{TD5{AMLD&9j>?P2mID~2;Au;b=#gm0A}18i}`nSYw6Yh175OvU!2)& zn_uIZ3%+-#Bfo-P9XIDYbt#nt|2Nu@OI1F$6k_J=-D<`0FA9-!Y? z=-JHTFoyq4iQI|N$em?Nv3yJ^XJ?2_2gTCkPlaeSYb|%e#W$pCUyS~#F%_Cvp7cS? z<`wI7T}X7`rscUhvW&R%Ui6R4qQ&3tj*Zi4m+JL92ul9}OWSNofic#(O_RFG`Q@}8>D`}CCbceu{BeA$=zY{GYE z!(wB%=<`7@dnf1R;J<2Q$l0i!stiG+@s=yb*PR!S)Rrtgg@Kn&%uFa@@bQ{p@VMx@ zTkga{Mb2fJ3)!WsWmV<=8+s(<3N4j;_%JYtX)m-p#+=w}Yph~nIXMo_G`~1dg61aD z=OImfm=tHy7Vb6u4=|Orzy=MGHa~_a)Po9^x>|xQn5sD!G z$LU7aZp98KxuK!Rsf$U!)9!Nn6SeGeQ3H?gi1ap}UT;CawVDXWUV)(|H|0IHJ{8&| zfx%oK|D0Tded>csy9KL7@~+lyc0cni8am10!B-uNfw4QW;xSHQTGOO{o1e^d+Yb1# za)M4&Ni5Qp+L0$iQ50CH z%>Pnkf12X}#XX&(uR7t%*+np%-e_$jM5Q82$1aXtBKay&YZC;|D_O|V!;>d+uj7I0 zT4TLtGWA7XTXJ;LAO4D1b`29jf>wC25x4noraIul#Z;%e57pOXYK}KQA_1G~(;~In zW{2oHB(qCHNM;+WL)h!vEv`SUWGJph=FltC5_i-t+>q zCo%02I@I4`uLuq8=A;?=Yh@yPCeL0sLP{Bnf4kQD$Y|JhvldSwMifMdum47No54=q zY1Uk;jZnk;CHymy$zNu78lp0O63;Bkt+s1!>^kef=}7F{&lYEf1Jx6v8Tcz#dY`g0 zmC4V`T#}c(Yd2ayzBP8p4)cBt!RRclfd*^1Z_sy(1IdqbDK+wZUs=+C^>FF2smROA zhQ<6(^x~;6Tk@-W7F(k&D80zSNT8>#+QC38aOD%QBgxc^W@wtvcjslt^0%O}I8*cW zpt7T<@+m5-B?;q`>M)Z5Q-2@(yCeFWC2lsr-vgi3*QvK#EnL^!N;W1t0;Cts$3QBB zelArNILV=0>eB~NH+7fNubS3Q*WV5+;9p{5?rU}bu1_bZvgOPOc8e@*+svc35A0^P zk8Ng@ZK!28-|6)5P!?XmWY?Q+D7tI6v#&VuuMCZ~m%%na0(^dDS!Qq}DX-p(OJb2h zrGC(+{<2PX>PQ~>I^}1nK{Cb0Y;HS6IjyX0G9SK!hP%zAWfNn~=$Ku*`PX#^yIaoc znx69U9BBL`E)9}cY%_-2#pA4Oj5Gz$(Xfx~?Sy|AB{|j47qYgQ)BYwTZ8PUGkQQ_j z8TlEZmCT&(QEUybSpnBAduz|SF0<^Q<}cL$CQD5D}tw}30C|*L*(i=ZUlQPk90?3<56?pHEw>C8neUn({NAf{6#QS z))jZ;ItAy`Z@FRA?jpwac}ZzkYcvmxart~0myhM)K^yg+gP_v<&!tlkK!0qhW>2Da zTOt!2=MJ@-_dhw<9nv+2s&2`DETJ2d-e3GT$p}vHMYHM}goG)mMqZ!@w{7OLsZw`8 z(J=x}w32{8w-ElG9%7O9B}4qSeY-09-&PQB`DRyMuE!OX#L~8NmMpSyX;K|OAW-04 z$+7$Vfs_3FZ^bhCWPsomPH1=as~X#%l;i5a#uCqLz+g3S0vD0?sAHCvR-U2URJ^GBp(Op6$JmA{*7B2mk$IGX|C@!UempdgJOHR9B^m%y76 zWFDZt1(1NUeoQqkH{#yiNsAQAS1q~ijQoTMt{-?SA4ibJyTBn5*~H8HkfnS+J@(G| z$jS?g`}%atBoJ5o3(J$W^P|;%L!!ENe$BYYnZ7RV?xJYYZx6J)O_~(6yE_w$ z-$UWA-8A;4BXl~hY(9+oSlis%nDjSv2fhP=zMO>n#rB|cB zL6b8e$>+>#@4;{;=0oK3p|@k=nQ4SG;19J2nOl$zRFmkQSe~vW4xn}xs$&BF)khyo zjmb^4z29F-=O^|5B6(_PGiyiOc|LksDe=1srp-*LF_omo^WBHaxWCj|m1{OloCU$= zY!n&Kh^ifCFF;U=1ULp(S992 zv()ZwK-#v59y7ifSp^4xuZk$#WC(|4=8@-T1s~W*S zCl|{URVIc9gP2?nG4dVD|I9bt$1e0!AJmxDvk!LbDQbShQ&r1oX+|}~JOrB25eJiH zgFI}0SSLECl9m61sE2ow{)ebayE2!as6EGj-T#1gyZC>%dG>Pdz##2xCukBSAjxN= zZYsVh34T&Ib19A}W*XL?;}y1Q`k+fFNIoqmP02HMc22m(ym+{gG!r9xo!AJl7UFjrMhl>`ybf-R{~Y{)Nex??VOnu*?UI;4fpH+M5-IxBgugDw7VBq$99lz5T66tII=(SNtjg z8YGEyRKTDP_f2DMI4s^Pc8Qg!i!nPmjS=?*T9;|!*t#=ua=eypZV<`K>icz1#!^w< z9Yq(<#q)HDL=j(ZUoQ6JiuBj#`r>Gut>D0N5VvI(?W87ba?}Fs9=Jt~m z47jI08Ouy9HPz)k7S(=n4mNh39Y1;g@YKoki#d6Q5sHJLE#;e{g<|;lSJ>tiRr!(` zdJdv6;DrR4K5Qm(mf{KP;cMk&i5LG|2&qn{Ikn23Igt!@h2H9DZrdB-(2n%M(w0j~ zW8Sm_xI>q?+Nq<7p^4`4e<;?rI~?aAz@*}|$^Xf|19;n)_D52ragBPd#4r|4tm%BX z_1H@IKFbEv3_(M89!b5NTgY}C@*|$cu591XV4ku}hP~=y#i0+UQhxxPv zfsoT!$+J@w%g7D-!`x*#*Ff7h8dMggu= zEKVxv3qHBia)5RWDNkNX@Yc(Ujp;DYTqVAMr^{*fGN~!bAnpM`{2|-TbJC{Lo5Eawom=0fQBGAWH_8PvQo;j(=P>>w+g&up z2&-#%rE1r4iXb{AGyDq1JH^lE?o(7IxdU2qr}*)$+!ee&#)$zPtJQC~Knw{t|M@8` z`CExkem*>fnB08mtC4jFb#)Oi(7_nx0lzi(PjA5b-F@)9x5pp;?3{O3DSmwuy zy)o#au@!qNVkOskr5`dgi$@VcQp%kH0yU5D+hc0UJ4mL)RdcXGwvQMyI% zc2D=~-G1VVsfDrfWZ`YYU696^AdMV2+>_hfy==90S!I`$R|>dpQ3bRW_;Sk{?--C1 z3}$~Y5`s6=ZRK4@rSB-oQ6Ois44d2*(tm8~j;C?;ZK*2-l3WYJN!NL2wtY8T-!EEz zN8#;-@f9$vZI%u@rNTAdIUuL*)}VB?)%CSb>b|3+7;7GV#Qt|$Vl21Cz%$zpB7bEW zX7{@E!Ga}6fhIB$Z-r8})pTKbn!&8&>%~_@4FMyy#Kp1KSv&nC*3`sAqGb|t(ZurT z@;eh7Cl1K{$Q#OBe+ee~to6QUp97h)SY%}7Y0l?kH2mAx9f{g^AkR)x3BIH_)&b^9 zfzh(KtA;BO2Gk8DCu*WQm2Tk613FJ-hpM=<#A_O9_r{53-fB|*aiwYZloCq{1fwvV zGLb`qg;uQtL5!c(gS&%~*u_;@xa24V?DexCqFEQVEM7m0jAse|eVB*0YMmH-=BC{0 z$h%ACt1nD=H|0#r#Ba*^sM0IUU#EWuZ?z<&>lfQ@udG}VUsqUp9(~_-E_UA}6FGRD zZ7|d}xYgE|)9AMAL^--=*aPiwl z*TWC!fz%lC_$?Tao2k1XHyL@?Tlp;+QqvJVx@9Ic}#3ID<(l&oRi>Te?} zkk9h1aHvz!sC?dAeH8ihGXF+1(jCW3MuVzzUb zeiFw#o9qY#>I7iuI-b#S8hxexSbcvJW-mk#cy*LhJ_Q2ijJl!8?oG+`#-db&s}?C+ z+z8x&3GpkG)S>iHzZFNxLen&iIgGBjy)fGNes0}t##xoim9X_W$+ow{sn8x3{=swf>~4~P`L~BNcaVI)jaxQZBil>`_64Z!(4u^}*FOGlkFQ{X zY{N&+l)-AoCo&6%rCJuEvpT?|%fH3(85V0lJ4dAETKP|`GkWJ;-syaaA1;Ptkf`E5 zvR`p8ia6+8uhhA{8hS*q*;mPa({tX!JYorDo9Oq7PW%Mj&MvRL!^!yKw=QHD)|$Da z51YNi5~5RP5cA8-Y4{9fMK8ntQV-wluDcE1=dGST+FLzsnD^LDOt5U&sStM~OOoZE zL;?R4ayLU5kyC|veV#*op7Voe_h+YG?bqkIfAE}WIY!IV2@Xy!Q#7IW5H|0_;vU_= z#@IA02Q{|K`i$)|jqT+=W1G5PLCCwDhUGQu?s5cauL&CtwiLvKO^>YN+$jX41SxFDAKV1TyOPIs~+&2 zmwiAq=&;X?OHS2YlV$aAy_=aO@A55X-%k}mYRc%$sa;?Wv$*80glM8yw#()-($N|i z7Q1W%dSA+4XZ0r{oXmfZmiRrT!3-o(q8_A1oq-B<43|+^RVUHHf@pUFrpWiyk6c5P zs&~%CDMa1{{wU|{7scGbo`N+zdStu{taTjT_;(U6a9${VYx8t}&qxgHM&lBOiFHbnbSd_A%wud4X%nxY-W6 zuHr(7P4(Y=THYdUM+-3s(1W>Yua2NSX9mbM36pVoM48vajtSWPS2iA-hX>PYiiJ{a z7M_}iPfB*NOm#zdE4puOD^A}$b14;!nafbTRX^g3DY6~N44A@!!2Jx>nbQ$(T#$qJ z^x#>smg;=(>G-VNv?)s;m=q)QlY3|XvA9y_Wd?owkVU1}9EU4I?5UCxPF)s?0ExQ}O-QUwk6`8cgwO0EdvcIp<3=%n4lgmc`u!1Jf|3L1pe@e#_X%~CVL-?>iwKO)i zBOZC(%N(K$ZJ7Y9qdng02CK%8dyoC$P}~27L;GRus~)mQe!OJA{_>4kaUZXm)fm60 zd$3``v!wo%#@3Y4#Qd%V69a{%GFx4Hf@@J~P2KHuyvqz78I+mHLG3J>QnmXd@5(pL z*%WfNYB<}~$G0QMpZWH8N7Nt9ulRw1GLb<=xPL`ujN(IY^(;$_#ALL+8P>V8_d>hkoMejm9XDAoa{wx-iL z4WF#t1T#TNkZZo}bwsA!c}YCQ#V6dfMbQDhPl)oMA596uGbK@{_3+v8; z4P}u}7yr{9Jx+1r@Ax8LGBYxqu*R88Qb z0!pcegZ#z3RGU4ENwNa7gsT%V1g~-g(rY5;oc{-3K`!Vo2^Pc^AmVV+TAmSF|vCb-$S(%q{ zlTFVinrU~e_PA@|f-ncKc@JWCuvm8BesF~^QsouwCT77CQ*{)5f8D6O_VY4etTIv2G6^m)eiB6y}-)#S*Osr_c*!`gg$fs?D5W?JWNELFuy!n zdflzL?o7mJzAhnKYpRS}aZA22(~0iEUhh=dAc zOx#bZ%l`ndMEYAf!3FI8z2Bz*DJ6!)kZb;Wyxr+kX3hg%$?}|{v5jDL6G;UxQ3>(j z=tKo~j05oi1qkajvJM~gNckP+g0};wt$o?#Fk*|z;iRHdFv4I#VI4tu%o6^&QmZb8 zz7)Bxj1b=HOGk4u_dZfn{!B z!2VUz!6t_IXG$JxDl;>es~Km6{B>>n!?EQP*Jo&UE8;7vPL;TtK!!>ATyoDD1vJ&@XWr$l zT?6dXUhnPJkGxrVSfTP%uhKAjY*$zh)NY|x7jcEv0>3h}-kDqfxDIXL+utZ9;n}ie zm=H2|9-Mcvo>H5BG$PYMgj&X_qXSeYUR#0m= zk$xw){vA?Fc&qLP2LxGFXV{3-Ut>AF9527BWYOG|8`oxr7z`_!FSs>;WWG8NMw6?- zYBafStXFQvfuZaWs5w^-OfsU6yvsDofqZ=+R)>;!WY^+{-B4T0>~NbkT*NQNLfRxT z(*1~!epPXjD2PCwvg({!d2AjV>f3Roc1t&#Cfj8&?h|k@nirh+u!rOWdXM=V$FA$% z9NDwvl4NZ@G7_u{JL$+M`)7VK^eUpUSrit7aK=7Gy+;b`M-k93LnL3E47=ElJ+JIz zyIPw0HM-l*3BF3Mn^o85bNDvGZ|!T8-6A7cc^sWneI+BWESUokKbMhE zLr|V#Xj%Y4&&klg6#IWZb_Tlxogq4P&2+#^eaCnyTgq<~lW)q4twWZIy6&#KUlD+R zn!w3JTm@f|oduikm*ylb?t5}Tl03*NG;b8Z#f^TYW5#-%!BefG@7h8J3%-F52epAv2 zR>kC94&YSASyu2aT5-R_JVXeVs~?Gkm<`F)BbNJ50f{VIu*%uS(BfI*~(v7wVfF=D_5yq)?}w=Zno+%bq?tB zJh0ER4VHDyOz0mDwEE+SUMdxxHUd4EVW%^PK5+=>DKv`-0udHA(a2%p&pdTcEA=ns z&w`$igRH^Cd8BE0A>{iw!XU#(Riy1!lP(@VfLEw`#Jdd7Q3Gb`{JqebrpZ zw>uWKc~sH|aT0ZsCn#=D%b|yVdlcC(SILitWKn{y+lr^~RIm9NA*$6|^%;LL)BKZ7 z;EWB^IsVLETUwo&3v*4C>^GCXi$%)2e3v);W$*GAu$AmsG?IUJFY1S(WP2CfyKu-o zyapHFOgmy{2AAyhhMr)C9Qoq9;6)gH;gA`;(j||<_Uwy$y}}SLUXkyewf^=K&<{$| z8}eYFg`rk1{tN4R*&D!ESK=9a^m-Qq!e1LZ0^DQoP!hAjC1mv&%wt7}e38qxe@SnP zJHSo#NorkP*mJh~sp3+uPATx3?g1-iE9nTR#=psIAK9jEuz7^~M1;D!ncAF~=BW)X z`euHim7*I=$FDWXQyUJi8$Vct&6tZaPhyev28EXu0H$Usr zVBvG)v^rFqo;u6w)Ww$JwVR*7?XqEOGt9SqUt`w^RXGD*Jd z0kOT~9S@fvD2K(}|i(IETP5of?`oHW^wAJF_hRkE8u(5cQAy3B2fmgz-xw-BuX7IMJ? z>nKs(GG%#n%fv=;F(wPEw&iEl+%zPQ!cMc`JeQmk;u)yR&ii)`X00DFJ(6L%AFJTDdj+oUaFL$rBgqVJ;8Y-@$q!lWx=tJTK%~JE@+r zaHTCpV-m6Tne>lsRa0;VA^kw`Q?1NL=t#sU?1GP9!$|*qOg?)p!Whc-V9(r-hcu9m z&d?|bmGn~+Y~~cLrBXLCH{aW}jwhbtb^<$jnw5bJ&qWn=Cl>$IB>@%yU!I|iIkFK& z1QA*XT@+qcLIDq}R2!%qYHb%Q!rbk*!NoEmMlSCc9 zxSXDh>pI#Tkq4)cdQVZoUp{id+!LFzFyPqyS^muNR@zac6oAVu51G}<%@ ztUU+{?t)kc7mUebetq1Q|6xzfFCi?mWy+d_zp#|Q#r$8C^ur+8d~c6d-L$Z-@!e(a z;8)zg$2#DeNj7oLYyT&m>iwqlc%Ad`yJP1px*(8c5k-AGjM*i~`7QaPMIScI`-J7KyA+n=B zwsx-Q^XItYT?8qaZlx^KFt#R26+SXEYY+-8C!RO*#dp8wV0nd9N1Y0=SD3MNlNA^_ z^s~L});DS=p}{9wW^=M;cL7%u6l1Rx>*+sGX6>Wm{_H4T)gpyRX!S_qvY`<;;LzkG zLpM^4N)#uZsvfoUicMpQPL|hkX;}aI{Ee86v`eT1?dvWrQ|u2m=}?Reh9&TZ9Nm^Q z1{+V8Aaoa!m>16?tc}m%}kq?%hynv71{xe0r!jZ71|BN@g zCzYl@Kbnh5G9%VNx8T#BMl$QivunT=wC+3qmw%LX_)-26m4kHHZYnuPfM1J^yzmV-5?^)PeeaUoQpx@l)=NnFF zY$&uA^W{b2heUz(YrNxvyt>fx8d^*(PH$xd$*{>!2@6xg5dDEEXd9QDu3zalMy9uh z%(x4c(G(4%edEN!kjc@UwuHzuH@*kj(=?3#OHh%?9=IXLmfa28!On*3p*(yAuQF(V zX{iR)-I1vM2fA_r7%U14vg6DRVL8vB*Yvy-2y55S)ANdogh_L2&S%{%OF?k|x)OH- z7`R}XEXxIH7jXO@+>C05{BMT0W{JSo^Zx2HS-+VPn_dx}wVSiX_09>SsKY^W1fjqH zj--`Gi2m;*ewb_M6XV}N<@bBee5NH0hqJa>ID>*rdSs5iv1+h94oncRy(g~3oPRTU zAPN|i$!re6B35Vpt0~9*?u3o{Nm!oniw$IOsZ7D%CjNL;t*jf_9*<#Ii7OP@%GQO) z*!+ow$o&DC5vSSGcJnI=l#nqY?cK)IiGd-Vz)?v74bVCg-s|`C52B%0ORXfY`SYYb z^oQyc+hhVHQp8#KmBkpa3ab6Vxn23y&{{Q@2HS`+r$ECQ=XB=5SbKD4?6O5)I3q>g zOYS5|WfEeDLW6g`uIJp1g0Wobyj_Ar2Kcq~vT)9#<6@Cby0dV^Jccipu>E4+4oql`0WofNr^Y1rn$|SLQP4YL}{bQS%$)}{4e*i(o%b$M0E=I^L zGvr%1wun3bpt|;5P+i2YBpH)qkq?NDEfCt<5cdeo9wzL=3g$Wn)G^O>sM94Cj(OA# z-uLPW&!^uwlwKF^8t9($$k-&ajf4hreb+<6bU}}tpa?oFHfh^e{Uwc0>K;LC(^ihK zj9^>RUr>-^b|Ps*^W!b0SeyAE?%5C>ClRSc^1@L4V8@_ONTWBASobgH!#|5-*%y8_ z*6nBWdqp=(2U#cc`rf{;2>QmNttIy0-46dXETOJHtZlR(Yd3#&*tyMFl4B{U^`$CgX%5!no&a3I=ljyuq-Laxu+ZJHAnXl8fGfpMJ>p;?O4*f{o zPc4o;H4r2dIVxuh+~$!OE6ef}a;EcK%?Z!8w+lFrN@V#=oDKejqr~`ufqvZ?$xG%s z#`910gfKD}iyd?uFUhO+$vgcX4Y@UQw{0vIFgj?jq{0Gj#rCWn)@yozGDz2&BUJaR zwQ`zRw)l0x;r<*Xup#+83M3!_3@ja)5xL6*xYhBunR$ExSCw|Et7>WLqta|M-?~=U zF4@QHZ-6QuZBM*o)-RgyE0R((?WUV~(9QiACWhl}^+CqLt&F)-2R?10XX_WMzl=>;0TdU` z*Y{nN$43@_mlj+2T2rJz6en`%?K^=eDipRqt|7{!$B1Am|HY9bDHUx? zP@d+Vp^367VGr}Z_Ud!J0ketBcf;boAF^eb|F*G!mjX4K;V!cht#I6XBWdXv!GbofX#ROv7kexV71aV?!N#S8J=6Aj8_Q^4D z#(#U88LT2v(b{WIKicNjeh25Thi)jUzi43sQ$WKHH2i8dqpOO!=mP(*%FCa}tu1j6Q;YMgBjwm&cF{PB%UE{5?+vdqOlJjl7 z)?kMbtH>|mzVP(m5(MBsH_&rVT~ep(Sv8ZxmEJRh%ewNtXQtwyS!t1+@Q<-a#q*;` z18nI;3Aax5^IOe*my>^EOL~8vcV8P{oC!m#Ax8o(F#;R&A`XnjD->ZTxOB)F!My=B+cT$l!KUjx#`-+^4&E!wG z;WvnF;ZB~u?;#wNYlvU<>R?_~(EVIZ)dgQ;#mitPTrc#RqReZ&{901HpQ{8GPgzjqa2U)J0@k~V{+G~D3{_9s$&1u zRRtRhV&Js>Wp7oj*iON8cf`U>l4_f5wZzJ!}pbj}=r6p`jv@)!j@6tm| z@0RjTyu9}G`s1}gi*7wSRq;?OWvqts>x;c68?cpeVzey9yXg};wm~w%+^)D&H*`N+ z$CX|mdrh4*aA;K`$xaEA5CV))t7E&l1D2EGpnfCe*;S%L8Y!RT=h0Hwd%_=TuROul zTXY?l%HV0LX+eaYRwL2zEK4h?fO_LZAV4(Of#h}5k(E+MG_UPF)NOmgXin0LpgDBR zS&_Ogf8|kL;mS_wxOBGN_)T&j5DmE9kK zf1%g>5l^Py58;%$4gH_|BKv%y*Zg;$5CR6)AA4QJjnTz}AhjElwxD0^&-4LmCzr;a zVor0aDvG`4M_i9(Uh@MyB`Utd)Bkf%39osjdn)&u?{-gfz2*k@RO2<@%u~GS`hi~a zJeB$V{}3CdmuX|ToB|-;21C(|Fz2>Ljj1e;A3Mft{xwa>op5Y1$00`* za}j8LK{B$F0L#cuuW2Qfrqs^&mRO`M=5K-jk&wn~x|Nzq<*8#!>jv>>RH@fAmy+tp z>q{r7w?g$+Sj9!g5iXr-i*fKp9p2Z>j^Ke-_ zv?mqX&D8>xoaS}`AqoG^Lgz+GMh?_Xjh9cmCgopSSj81zj`9iCZu)zHR+uBz4s#U} z9qM`gz-kzBX?5iP>fh#{IGaeB*Yp@ol^C1vcl}le(SWdyc(dvyrQC9MNpB+lDfUl`9_G}E6tv&H$P zNJns5J13TwPa5UN?Q)-X{knTNIOlFr;9M$K zZwW#cV!4#bZvu(A^nUV_wTg0<^dqNsVg0ZQcAxseEStQ#t0hv`T>&QSEz`+A@9KCshRQ z`|V!yf3YeR0f_lvJ1T+TJ6G`i>1Ut!e938%0>V7_Eu?TC-0Gtf+yDfbw{kokWooXc zty!i<7Ixm5L|d!OSt#dG#RRx?#+1a(>N#Yrw`ytpE7Ve4Usf{($^1$ebx} zUAr7uomxDQp5q9KxY^yTN%MR*Q*)hzwb*o?uLiCTw62}Pt6A90b5u&vBz}WCp$}Iz zvB8BHjx~>xM=XZKO4r*nH3J-CG=AgLfkFfPXR9*To=YLn{A~g)2haSB0q~# zvJ7kTM+l$`h4+aR#(mStu^(A2)?QCxS7*Wx#r=sAYaZY?)@vuzv|lEuuVMnLCLm;t zx1S}1T0+bJ*t#1F31*9Qc}=VAJd@%`%U7Y?gh6dLf4J_`ZnDq2rJ5pBjBeJ#YJiib zigeGSge>|VnIN@eHUg~;PygAmx-JVlcQ<%pSFh5Epl#%hvr?9ztn#a8NmD)F(XhqR zwLP7n{I#1)=!6$t4|@IAysl|Lw5X1}a>P849v&3RBqLJP8s>~79onaC>K(chjSFIp!s*FkldZZRb)wTPUBQJnqk#5Rbg9B}8miX7Cm9mMeP< zCF5T1CjGZ&xg{~rfn;%h4==j%V9c6`TPUpdZ&C&!uX!n3HteCKyepiA7>rNginK9W zyl&A3mKhdp&ljy}f9!TX*HvJjgEWV`>vfIMU(7E4%9=aw6D%b+WiBCdJTQ+~)5v@0 zX++cLe_P)XrF#0O`B)4x(=}OqM3|8M<&V6_k|3g^<|{M%_#3)jbNaQMvFKtco5Z>& z$KPG=3fn3F8`Y9JcH!B^CnjZLhvwkeKl6o8WR)fHUDJLjF1>su714NRX`XbJgw&CVg9)F2nFssyi)L!@)&sny zKvGz}Zd0gO7+%C-6dTRI(m8tle0$yf#uQETjcR}UT7{ngkUsl+9^F#@8S(Xk?&Whd zm;fNwyKCW&IO&ZX;j-m7R+vtJM5R%YR4nN{w&p0?b$hI3_3x3p%rQ$a)j8*x`p!_R zvuSFZiX8HqZsudL+yx~wW6;h_BxFo-S2Op7uiNQgU2YkU}k5&J|8IqK=4X}S7`TRUKhZrm`JVjcl41x6dd>e8het-0~=?oK6s#; z418paZ4WXq?4bRuM2LjX_q~|&aX80pI_4nwX0B${4P8Q>npxdl`EkPHsv<8eeU^Wu zHa(!;(11j!-n}tChG~FNUrP7b1<3mZ6;(F+ z#d%HBcu)aB-1hHure41v2Rsg`H(!2+ruF^$35kkxR-Vf3&rPeaV!QcTe!rcs%*MPU z=4q%?$aI ztF;(*6$kJ;RQtdBJ~Lzi8UjFZ|NSJ(tI(R#H(0ZOc2!)$Clme&!NRLjAVLQhl3S?H zb5L9Er?t0AYf(${aHx-_S+!qM!sqIL-1!o;0||Id_kF`+Le9wT=FvIw*|tZ|+ZgqJ zv=Qh3y60QVA>-X)5ojmFEyL54HZhik`%j%+##%Mh_ zt0>GOHFrD^tSnPO-*EwDSPWD&tzi>ryxE+3CKHeuaV8_~Tm--3x!-A`-0kL9kaJ{X zeL>uRyDLul+hbh$s-E(GnNt}P_kL7No`B;9nO^CA zTCUugA0xcN6=uCTS8i5eo(c=IE}mFsM!wuUROG9IP0jg!tRsOME`VHL5U-eHd!J12 zgXw*9z7D2#n9dn+>o{P!pMU3Pc7tjxkJXKBET2?&a%1_7x?>y5udW-}SbkmIu*UM+ z>cX+7P%d-2ApEzw1__P*Vo!xB0sD1J;*q!O8r(pa_Y5>-CX8c;+0!#lbeBaMr||jK z!GqnIsks%?1Z8G;Qg}eE$tQz##ibLxdwyYgkrmc167;rkWuHRi#LeN(?^5EvTioZn z&z}8hx4wFmzqzMxzRQMSyJ-<^I6<(t@47yP>U(hKHIzIVawB)UNR7~H0pF3`6OXx{f z)D?uirZvD2_uF7Ohw+=Y%fu{Z7sE%U_*b4NUg;DP%W*bSE6n(KSTyk?Av(2YfuD$F z#fFhY%NHJ9Hz1KINUkU7r+>n4)aGc%+|wD-gJhbyw$t0_RBU6ezk|Nj?FObeGozSR zc54aze>4l$M`(L8y`eZ3nOd41n?>i*tb z=>EQQl>57Qh}$2}8?8xnq80I?FM6wUh@TDbel47X@6?HKm|BwQj_yQ7SU@H@pnS8CJ4Uu_C0tIN6sP|>kLdV$#P>CjA8;`se z_onXRh8{Jv;!^8j6LtPs!9;H)yEIIzk*`XyPI#-%9}GXZHwLuWcf~b9mRIDnT{3FA|3^4C*>&Mkr%{ToUYTyCv0_&1UH5V z1S?h(?U7(+zHhsZ=MwkIfgfOOwFTJVH1}4PPE(Td=N3vaiG70hUR-}_6|^t`l`HSC z@m7lui%LSTvOuD;92K&o)t#m0v%b*$B$`|6lHK>~F=L9{Y%q%zr}g=I4*fhBNH84! zd6F&JvC~*MGh+$zX$*tQOeBqBk_Pf5iE^fS7N(3NmwT`O$~nHk;S6mw^7iuh)-H=z zd?;eeE|c{Sc%xeC2IMQ8;JP5u?TGX|Z5Qoyy3lkSg?ai3@de(R%>?j1mUFv4X7D=8 zv1glqh=q`aRpZXg;0CDm0@IYzG&AE$>8o*Ayk=1P_)1OO4nczV zlh6Gn9GK_iSb{s2z(Mb zMr2hJ_g}NajI`HPru+}t$8;U^WAV~Ic#yng;z_f9z1|WVtzL$ zyFeUwnjQ2Z5AoTq_nrlE59Ds;c88YTS+opPyDxIRy<@(dvROF^0Urz_^VZ;=ZK zn;1u5F}GDYz<8zDyqft^au6fDjviu9J4QHrHYVJ|mRy2b%4;MTbK+1GoA?35#Ve30Sio z?31(rk6B@eIWV*}Vfv%x6##Tthbw7Lc`+yq?13_dEOMP3xf9QEjYrVGLx8>*P>HFe z@^H@mB~+P(-I83i{I+YnrZg*BwDD8>{${2Ews70*+wRcuP`n)69qdjd+k;(Guw%gu z65cbz2eLI>ln8B!me*}Atv^YVYrV;IR+Y)`Z?W;)(E^GYSHx>8i17Y2aduqUNeE$# z?9X<+_*Yr-mJlVf5({9M^aW!ZVEei{+m~JIHHFj#p9-PWBUH zx39gGm$6(5F!czt*GMcyPVv8tBF(N-HO1EEIyO(9-2WS9P0_F$1g)`Isjxt{?kp1A^_Tp;0D^{zMS&q^C( zw`XR|(M#B~t$bo}v6{ER#RXUN!9_n-S-`v`4cN^eY2i}H4+byJm6_2m-L_w>k1d!@ ziY;HU84ZcZX0ORpJ51|=a4Hc;)1}pt;A?J4Kr2}R)tpVK1m}fov8Cip&g74bXD-~C zeP_6>h1TgPymp!!ybSzeGUgLf?SA<-_I+foBQ`9v*(T=2)a_(7%M-HVng#G78uZ}_ z=2J*DE2~+-Y$A6-Z`0Q$QkVvsXJ(v?tcDR`dluqr>{o;~A;ytTv^4ahMWv7?(Ybj? z->t$5VNjLC%Y={Y(krDk&*8|cdJ?m5q#maBFn>ZDVv$LuG*p?l@_7z4XB8-o0Q!D4 zYh4JLO@I_w7e>ezI4f&ni0#`IN5vit1$`CuU|Yx?yPXb)T}F5xA!elCwf+P1Dl4ON z@F)Zk8Kgpb07h+L7zVZL&x6)A78ymXzVMHe?B--bzx=fO8BHK28eFH)Si)@j9)zs> zC}P$NF#g{ZM@Re_`gJ*9#(4zY;_kN-bt_HUI@bIEft7$7Df=wz4&K z)Hsr%1WvLUdc{b34QW-mQq^@4^M2H7yO~U5N=28aSTwob%P0nOf=qs-(1sq!-p3;j z!aK*G7-0E7^l&(qAGu0c289;Ii)nxuUt|S7ND>*A-dg#Zx%M7Db6;>&6f|R~Fx&4X z=rc29^raSGHoD9qvhXR3;Q_9qkMUyPleXf1SJBsa@hUZ990{b=*WP4j>|qP|T2^ROH*3UTV zk+%Qd+cF;34G0Y7%LcEZ>9Szn7=fBuFtyu=@XW?CSM$^Yib=i-sLK8<_)buo9xlmTu%)}5b-a5P5Q=6Ln!+aR}Nh0 zUK_`29A~Ckw$7Bfnjy9(uW!vU)MVy_=y3Op5H``ibqCj2_?8u_zx=*6yH#V43c~R* zp}QNmU@6=rQ_$_{D9?~R_RomF**_<)Pp9MuRD?dhefs%#Hnq$>tvmat?4>m;j1-WM=LBq3Ai%_m(sP}Zs_F9vD?1* z?p*ioT=(u=_bySPyn90ME?fHHgbUeQCU*BokzBlFes+~+E51#Ib}D2QtRI&tcE$2P z_u3R{vwN#n(0@YE;>VJe#1g7&kB>cpcBJ{00K=>L9BW*QTW<_FqKB*K!EO<;Ckl8k z<-Z(2lHB+53D46_`i-jex{+q*35S3ARNi}1X1*Q(v%=IvwCuHI>YE3&zOcviG_@#L z7PPBjKr+c~H#=QB5mKKqU#fPQf|aB)=xO;-&@waQL{@D8u{nd57O7%$LWxWr4e`(h zjBmtwR+h3&a|Apy51RrkGFCiah<{3@$s~+8d7KfjJV1* zoWvg@qr*L0jXa&mZ`gruKFcQ?708rss``BcK^L-0JeO`H&628se4}jK7`dF( zB~@7LUltr|^4bbzB`R(y_L^GtBtObTWyUX3?C|}80n3`AzG^v{biyj;4u}8`g3GY)u z1$}zW@A4S)FOGW8Oo=k6h}Dl+vUS%2GU(k3#hlgMY6N-lV*+{kS^;uqdr)|qbfM(js zQjKgf$<*3%_XQvYuB-vTB&b3v$y6Tt3f0*OCNV^ZSVes)QiQZkZ`G6hE17{U32%r( z)f*{EW~!q}j?NJa5Aq~j+PRch0d6RF}(s(A8E2^-yYw@fD*e-JdK-rDdCbyRV0E)SR72_t{A=s^{s-Cfw zS^}8xUA^kaHoF39uD3J1f#ck&+RZB{G}@yBEQsZpUs26`W{qOzppyFG)6y#o!Nq%#yiUEysL#0KR=w*m{G|Mjb8SSKvY|i&QXwA3 zm(=g88v9lt_9I08{5eGNyT~fneF3pTt^86aPGg9))s4aZitE%T$oE+d?g}@!QC54* z{f+&MypCP#`g~Ubl}-Vxio8@mOmiB<e|n|<)^Z1 zi<;Cj>Dv?p7WWl(p>W-}6d~{ZU^U@-av>A-+F@QjTjn{G(`VP>ZWZCi*}aQRhIE2j zpNT9Cd?hs&^Jo{DHQrwGXqI9-A4eui9~kM~s}l`gQw`rwcOH$yp7NN2yi#m43hMed zQ()a;jR%nC89E1iaQ@>WC$)Iw9Fhs=nA zpDO`qYo}C7EwuNhGuiWpXe8F30@_s~Q!GXRQ z9?N0j#iJFPuoU`wEvfUY3VKfs3Y$L;__P}t_xGCbq0`v%n`=6k@SxF&l{9r}1~XQ^*ltewt5jND4g&5(4vEO2gqJ%1XX_I(Go zv@nmw-}GK_4?tY7((7r;(wNQ*H&B= zxukUQ`l{uVrgs(CPU%Opyxb1oE>DH9xi}nkxX^a!lDXiWE-0UFJB*U{B@vEnw`fXw zRWBR=R?EW+e|97fzwqJgb_IU5V_6}fb@1W4{P03M6*40Aor<5(tn+jm7SIPs$2+os zZfcc5IN={B8B8`GA^+=rS=OV^i|}1#GO{V@r8Z^T@^kUA3E#IEYe{RHM-s0i>^4@+ zrS3>y*^B7%Kr|5IWW)v zuvF=`{J`|>fasBW?x1;JVR8EqOqAZAs;@L%ANN89f_kBv#4FW^oirO{HqgpJ=GwUN z9|jo}w)80bo{m@`AtJ2-CMdMr6y~kJlI$LW7CQ$ZUZp!v9WT-nXAluM*o0QP8j3<=Pfv|L> znLzaMsj%$-uPj+gu(45Vk{RL(Bed!E>yx1r_h_~M%lV5FOGoaHu_%Ez4@P; zEs1{ctzO#g6JWUDL0dchbGz&r~*0S5X z4{{2=vp|Ete)6BnU z+B*4gOTk}h(D}N^sO6lkdVY~-jbbP%-X~3CY{NCSm+T`v%DYIpu0}EUc zLuHlJzmu;`O@jazWiqX z;V*yFz6`)tZ%5ojj@gJ5)5SYIj>+Hg64I--Bz58p>NLpm*1C~wa%MTLBtRwr&iU5< z-k&2C1J~l|%P}`l8*RFAtc0zNUEg3|(Zc6oJRwX~*sVBoynXTUn9RcHQS-fL@=yc^ zVc|}OUaE?;Eq$L5jl)a}$7>++AO3%Yy$O8O)w%zlWCDx`PONZ^TOB286qjhYb;d!> zBs1_GnLw09Y=uTcQ(DkQovElS!AXSS%U~DVt6tlS_ja+j_SUx6g<4Hm1CfBZ;f~tY z6Gs%6BDj$M`*XfC7;XFe`;yFeIp4E9=Q+=L&a-}=EAiQ`YEEn|b@jfj96f3xJ$9l# zo1T@|up1OvgL!RM7uq;MBQB$!IYm`I$OFY@zjidWhTsTM%+%1|}Ku|wZ@^4^QHxt+S^amJn5_*8} ze2#mIq65H+aa>uY%h>W&@6sBv{^i*@heRduu>a4zKq{1GruHXtGyQ>89hBMI0FTY%YCR$=Q8}mEg){v z@QFpm;uF!insX21SE!$CL$z6kM8e82@ns}l3e#`>`X}&_)?z5`I{^MKh(|(!l^vkG zgLqUCH7rI1I=^fye^#A7WYTA!4=a4Wfan8EVeVMxqb{KOX1o3+m%b`QZ$M*1zIwwcfWT2zdqCq!nNB#aI0I}py(H&A}IW1sS zqrBeRAz92o?1^28miVBDK0Px8h259T9?H+2{!Vu7q&rM|=(*zA`%L>^5ObIsXE(JR4h$?XG_I+%ooj#k?pCY6sPf(m-cH1j4A{~jorvqvpcm-xO6ia>AaT< zk9HKyk#jg{N{A&@UTPcWrPcbLF-%erdQwvVxR@pKXE5k=VR9ckzvaN{}OuuF{J9T1FY;y^$J^_sM_19k3W(@j!{e(0Xmdh<|^o0aBk5sLu-wBuFmr<$jahHsI zoGy2J^P{7o<5R=PQ5QNYy&tjUQnDxvTnP5wqe3|^u!WsAOY9FtzKwT#s_h|*iJcRN zr@j#-@|;BY(4(;%o!gzLeNYM^5pCI_-0RZ3qn(nVB*+~~iY!;#>aU9d;uBOcY=NbXc{fk(2d zA=#3ti!@q-7+BfQ(S-d~qgDQzWcDmO2LbKKmD0zzg-aJJ*6U>I6Mn6EMN}+5e@xgi!{#+{&rZn2{dq51;(jle#@_njgk$+L^|6q1P7e+W zHD=0vrDWee5e)hFo%fV7bMT6iord5Sazmz118;Lyk!9`Ct4*)Mj^>606%uan3q8BY zJ7Clp#U{-xNtUFissri9q>EnLIwn5SM2Hf-3EMZd>Iv1`uB`tp?dOjB^sk?_sfmK% z7W-rx)w;M(g_Z+NQrdHfbVhKwrs!;VF42t2gNzqZ>U)5byHq$%@Ic}iuxdp3+@s51oqDh4zsDdg7E%ci_WKKluh#L);-~ADh7m@!_h`+aN31 z_63`wj;=mver_v@^X>E!Eq){XaN~sK!Gf}&loJV$g=Zww*T+JaHl?o#JQyDiQgxfM zG3zvP`d}`*N%YYBK;MesfWe8#+SIw>qb+YnvA_c)lE~6TX5aACkj%bA(@Q|o?vG`G z+Gj?ywC%S=WuV{%GS6k{3B1`=8g4Z5JRx9tWXB#pBz+k9P5E4k^6MvvBHmV2M`=Oh z*{8%!-&WOBxZlJ*f$x^C*96*F%u#o3!rc-56MMLn^?Ov1aclJ^!_%1}xm1Xh%#D-= za`ISkXl-8C0u8STdgZTC`R#n3h_HgbLkSGh{h@edOX~Dk$K_bo+mkdJSpWDnI3}uN zp04PM4~2JB-Onn8-%*ZQ3`*JTXPOP*Bty3fRROs4mQU;$I&4!ixl&hQB$){@Ki*`p z@Gb5PB*ek(hywkyVqr{)Qx);tSIglkR99>4kXgeW6eI@+?2)Vf*}SwJ3LnLeYTJWQ zud!!`&L+@wD9^Cj*15OIl9JL1gglxs%_r;N;p1~ncx|(bd9Fh@ z*i)03B%uo-u*#~vDWyBD*=T6OA(wNO@oKKm#>(m@xUn*V7Uh}{#LGn>XrB7JHQ68N zEgHu=@_im;98Ygb#>yK~nili)E9;vGwVqDg+Bh*$MP!cnOu-(aSu9 z$q_a;?TuA9W!Yp!NLe5I%(^?Za)1y8n12em$%>`|m_`(#!yaS7wR!nU4@@gPFs-bz zU|MCtl&tt1nBtPp4F=P(uV^`Z4oqN)07SDhg)Jv~v0-`!y9s_48^#Xbg3g;}>^Q#U z4fMw!NFAyi@NIIQhC?rgo5FXRHe%TsvDoq%$7_7eu}~8WYqTcub2$G*HB(?0fk)U* z{jqQ!@?*<(LYs0!Kurs8?8BSPMcBEnD9@$M@tx%pSCqJIDsY@IAsoJvBv| zx7wmrJT#c#fwb{DHMD>#SHy>(xDNc>NkLJC6l@#DGWO8nv=S0>Fn#LB;B9~}s)n4m zEY0L&QD-d~x@Bxq9!!dQyYFvPhAeTJ6G}jcic>m&iisrD%+weeO zYgFZK$FSsN)-M!8tKaTp^egYr06rdAyz(}m5u`Q%^lBF)G0Dzi?(j8X-9pKN<|Z28 zp*i$@emV8p|CR`P*~TE&^Q-~-sHJQcq&51kSU66l)Tx%Dk?jk$NKNF3j|MiDoaeu* zs+`9Z>zFk>(6N;7+}TYYNQi-;+U}})k*CCI6pT&a3GE3C+~Vv$=~C7e2mi{u|7!53 z+Le2$ygR9H7xi)UN{uW!0&k3M$@}l~S-9kV(G`pWLI;XGr;xqsDK!D=|er?NSM9Ve7<)lxL4A8%;wgmua=azg7?*oN+=ODU;3szx?f1@ z%ulc!TQBhoHzGX`Jm}PzJ6~XhN8-Wj(2d1|vCv%>XH&$LeV{%>i}z4};DOE2^VS98 zJTKbTwD=&D0e zw>~CTTiBr?wNnLs=hRl{!E8viJyWLQWj}B(fcX@pd7R*uLal7u2|zw_SR> z_QbL4c&jv+yzN@@E)kuBbG~~R*^Gj7s51}v2>;87)F-=k?>Y4&cqexYGMt!nH*FgC zxqI}`$zfzgAK^DcbV>?g)A(~SXfgJ^$ZdJ;lAm9iU1M$iPvl^Mf5^Mrpf+}0xvHKnlDpjD-ZRuP;c~4 zAHsb4J|<^5aZ|^vi+oi4df~mwfv6d^6h~c|fXyq+H`Ob0U_3OH%^^m_1`C};WJ{o9 zIJ#x19JxkW!<4I{+(4Jzar;>2=Jc&1gDWjq#2V?nz}m%3*lju2be>&4@9Y+ydM}EU zj(MbLC7+makOR8I6D%Y>LtT5Z$F`T9FwIPO`y~fa^=HgR3w7UwBDym2sIeZjS{6%S7uC80DZ0XdOEB6bXF6q za#W#&-kr=lUa4lCMWKI?HDcl&E#+^0E_ReiuC3)0)L5!T-LY1H1%MoG-@a_$a!M_n zwmk}DX;SpV7g~?$dbotKx1LnuZ#}dd*mI6Ic#@cWh^t)>xBF3=Go z&1T?HXPg~&#GejC-7Sn)$M1gSWsikC>32$p-W#fXB@lB=dUB&@ajkmqwyCbrMrb}Q zj=DYwu_^$f%lUD8xDb7Hw=;Ow^(n$kQCLJ@Ec=RZ#lmu5ukT5&gUPYI>38UWEssrX zpD{$@=9fC|r83h=)N?hZ8;}eO5cB2_q~`QmXNd90+PJ$#y4MQY2&~ePm$$YnEA4kI z8J~!%sFD`1`Vx+vR(qgB)+9X#1*;yPPM?`-$wVF3hL@7AANp`QEWeGpkG{d5& z_v8h(ou4td^V4prDpbf!d0pFrYHiJU>MO1ZgZxAz6s7(`&%bSZzKH@L$MUKFFI z`Ldj6d+D&qB1YyEYPgSg8of{kjPrHV!kV+Vu^?D)5W=0vC{zqU1Q`s}>u`7~_()FM zqsq)}4}y^%qG;_26NUF`TDZr~`w*8Ukw&4U*Di7#-Ab(PNsMNgpiwSwNxg`@gkP1!vgr?v3w!!D#+lq>zKFR(9{C4V!eeS zTK2floL%BAdXaqfKxCfD8R(*(T$4vMe?=98(MM%T9wWI%v46J|iu`k`JJ-Iop6%+> zzw~T(a<^Hy+jAE{H~;mNx6ui#c1wVlEjD=smDYB_a|$z3#EOAODI#~SX-KiyTR0@< z$+l8hVV(Fm0Re4)lrVjEWq87c&xX&yc?l3KX8bQT6o=1YqOjyE?7LNkcLIsKgcG*y zAa3bwUsx^LGT*jkyf!ol!mS`P=q*-1iP!W}(~D{NEbO1(_GX}knET^)>RsK~`S zxqrp2il6n)fgZ>D!Xz(-hK6tXn__POpI8@d-Sg-W?F%ym@zBuE_wPlHD}{Z!2pVrA zujRY`+mTa=o^ zjmnt`#S|=PdTe|6Khr0}2Fi%1&i`jouGY@E$V@z5O{VAN6@iZbQUf%v@x_*V{65{N z3v_OV>@YXH6Gn^Fs7&O^K<6ypG`jphGuTKr;|_=WX!w{w=Y?Eg^9D5`RQp|l&Qt6? zsvzv4zFSse&Rn;DKshO1QDa!E-DRszfboUe`#FBa5B&tC2xj8k)#yuKe=EXhY32~1^dTfC_OOkTvJ}MOYJW||8 zzB(s;0>M@4FGs6ZH(}A}WcVT{Z6*7|coMyQ5tb`kV7#mODh=0FXzMsPIuizYQ2)yw z<>1R;G;n5Jeb=(pynx-+9MMFQi;h;T1dE>4j)vrY#kxcj^`%%Y@e!)1LfGp7E6%QK z8-_vOe0Cz>=}x6kNODbNsyIlXuH~^+YgnLaEq2tA0!i^A4%UzxeOpSxV2~o= zH>Y7C;wKxI@O zCHU5*2h7Tz(TakCjgQ@7R=$AyyBl!Z)?`T>yNHx@Ug&M{! zCd2(CfFbi6DzNi)?5nDz*F1s);NXwn`WA%!F3aK+$(?AY7Cojib`2STV&+a07$JIi zZVe`Sw7@OF_@Qt##K=B7n13)q2lL`UkYSl?5oPm043$RKQu@(A`y*Dm;7dyn^=IyyLplt71@(whNkmxn>p%{{ZOt&Tr=EW z^S?iqozl)i(e#*jc#%JSoR@bO3u$RmCkCPMp+vZ$EgFcuOPEcYcSKDDIWFAWOq%tA7KuhvLc-M{A3Pze>N&_eC^0vRq)~BV z^GuSwUvG1*>`jki6eSFO?)fsXaHHAE{6LNcRi7^5+ku8;zXXE`(zmIh`e^3B z*{O4ZWeKGo9YZC!Tfa5h2jjPwZ`W{@#QZ4`>+%vftD-~Hi8QrPLprD$(lX2;O&Ig1Q$!4=1i|_}YNlmZ0&Om{oOH+gNkjzzqQN|Y4>mgM;|8xaglgROAFxX=6Aj8 zeIfQV*ES~Cr>dQGo15B>!9GO`!- zz9PjbtC=tpMO!73)9rkhK&*`6S0f`X2*tt3n%v}*be_?p!;M+& z@q9aNt2Bi=*ZdG+kFJ8r?Eh4QLb4GEhz)AoVs=ew;_J3-y?u zW4wEjCB47v9qu!KQf(z^<%ZM4Td}K5=I*L$=OM;D7!%K{OSsoz?DP6T4ZPJcOCZ3| zzL>sjIo~NtLehmdYipKa&@LWnO>(L;BrqdCRQ`};_++se--RvMn|_apY8d4!IcRrs zNdmPnQW}}qo7`kg1Yfd=s#7?)oCv^*Td4ecWLc&mtN3iMILt@7UbZ!wgFwzZ?c8Na zvT{-r$U>_rnz^84;qi%lzwsBlHpMLunLogESYsCp z=G~2KT`Rr0&TK3n=%%yeMXQ&IMnnP@^q- zkV~G5Y_>O+z1Mcc-1(faV5$Uo1sy?y%}!}`1Thfs3LQY_>d*XS4)()CED}vOAEw^T zF?YQJZJs#Ioia_sWPoZom-LsaH^>9$3z#k>+dDv=N`u~$n7a=SV;7|hbIDpft5~dz zVliN(%HsH;4iGM9qdnl;-fQ!G1!p-@w?yLg40F*$C8F@g{D>V|LoRlD*~DD(EH=S| zUkV217ksF`Mh7+!2xs9lS}>fbAF+KYtd<+mWf9|`keUl7Uvnx9$4YmOXz4dVk3cqTbeY(KNCICXV_*wfMF(|WN^Ue?94 zI5~U-qR7TV;Im#Q5Z?!MgBvHFP-^z#Z{|)ScGb4;B8ntm?I=h}gFlsO-OQ}rR%jK4=!CLbk?5g7f%6Wk*cb+IkvEvsbk>n zlx=kTsh!c*Y=V)ZvtCw9>_%9;=AB!_raTzbF4no1(Dl5>*N4pN5IFLvoB%5|*{R61 zHbFkX;9jV@dB&=zM60zzzlW)_1*{&Jo4HdeJuc_nuHF}meTx@ZWBJF%OQJqFzGZcm z>`k88NS^J}$e=}T(4@;A;RHpy$amTu0*wnNs$J7_-5t8&EkgKT9oWVfD0)_V^Q zmooBT?F+4$8~iMj{i%_y;miD~VP;}v0IM$edUB(lq#;kK$n2Yw8o6=e@KQ7Mt^)(^ z&itX6w)iffh3v!{9UP<`$|%Dle48~(s+dmJ&goIvBLy=zOaoBBki4ybppQxSR43CM zWQT6@D+d*UUKdoFg*2;E2ZATLQb#44aYiK_L-vxKfe|d3QI51`CNhUB8Q}Kaa%DtwXr9i+1S##p80m}NPM_I z;p@`xzHGxZb0j(9mA2Szn3j#h+iRpsxE+knj?Tg^g`v|sv**qO0}mZa1>{xHL7lB? zup!dH#44G$IQn^nS#>=J>FQy)B5BNKqLM)So3O&$pg%4f^)jO!qa)jcmk3UQ-8OrIcwR_E8 zA}84u@?8~~z-e1Wo*Fr*$dkn)1Y|-!a&@IGa>w8z_Z5rC1{U%(yBWzz+iXuw70;X&579~5QWjIvg3+p# z#acHMvZ&fgc9w_v1zGKT+sdR|vTi6j>v~269-B}fefT{#->Cbf74hP;X3nH$`ZN@E zFntO@`QJ zEa-azhd>MVUNe+OHsMvTdF(XdAXZOh%BXt)pqsIV<`+wY}*?z52gxFt<=}Z(S2tRPGz z23*SD#PjPlVyv#_R7Dkj+|i$;{sgHoT><}Dhc22!ymZF-;kx%($12l<-z6MvGlMZb zlG^2tn{};RC3Qwv(_tG!ttC``^+`|zEUoiQabww*5%@|po6S$Ls3PkFo$ENvE9h&i z|8>TNC0B*{)p=r9Hd`AZ*y9?Ks?wuu{Pcl|@R0G-!BJ9Hc3T*j@$?1gT(0D)<+U&5 zkDya_p~T#^W?qwKXea4n%MTS3)*Q&V^v-QOo#b{wj+TyCtz?M+v4TV5@?;e`a+Fw>BXdTHI zgJQUG`PLKzWApao939+qm4MmGb0`;BWh3!kuEdX!husT;7QFZanBZTOty;=^_)g@J z76G>oz=mtHC{dCw%NcJo|K_uKt-l0;nz;svqDAQbAV{+n(A;QiPEzGY-8}QqiDI&6tlB#8^UIwwD@H&3jE-jrT(5hCXw>dS^vVg`MY1 zL!jdVflc>k5li8;`b>YX&^v}SYXFa>E{qwt79?){$O^E)BRGC+7q*!dXlLZncr(9t zVny4#7?x&jG^H;0%t%<1s`-78RhfYgbEltfLq_zOaj)yq?gb|)8+AOg;ky-4h@Q<_ zGnwsB**Ir#0GaG%<5ZjfTdp?O>v(&gTFR_%8${4xg&$A4FD9~It4w64T7GyCEpyyc zMb2t>vy5ztz5SdIEt_JqOaDM@kz8(I8vN#j8$V;a7VrjDM4Og;dbHs4WU>W{80RRk z&GQ5rR~lGz)#+@pTg|1Y35y&ezT@y{98Q^qt>zA?v&3&BsM2FA1lelNM2lx1AFxD# z!^t)?;dxJ5gsTdBft8g!`D#JrWJ_(c99%$0lHBMj9FO|9@;vDtmONp8RglWp91VQT zxAARIi+{8qao9l)cH*b@5n-9cDORLV1J7vAAL4{hlaR#Yk={en$>N3ATdM7yji~XM z-N`ZR0nB?6!UE{*1V~^tlbULGGsN;{-CpN?gA4MO=;Tz%d2JeWx zQuy!wm(IN$G|;LM++*Fwx)b05hgvO7k5IbVl>c%+OeneT3nRI1F-Qt>m=`M4k)6y) zY92XFnRJepB4CT35kdW0rOpVQFQi}hj(QYJuxo{}+z}UF!`3#M>u6>`2~O&Vhyw=! zwk0tt9F+L_8a$8ek#(z?{PX<-Qh4d$2%W}3c}V_I$rDFD&QGDD8+^7R)dE{&L2s2D zNOI;N+2s#{oH=scm?IGmOWPbq6LjH7yy$Hgwwg*-kH&Qzu;9&K%T(v%UjGD@pn3i! zjnU%CJ5TG$e&ny;Z@fFUyzp;h!GTCZY{Hy@Y>tJR3%zp$9!VuDW1R-})C$Jc{nxkn?@*9sa zzpJVXgotHlo&JvmK_IQPvcu1_HbKWdTgIXv!PbOkqXYPrcBEWA*|1I3ceVJ9O@I9Z z9_!3Shisn9igMH$%YKb;d4W(YJhPHjgWQ|>$-hA~F5@y1r5d`-VxVNtedfZ$Vba?z z()MwXHh&6wr%2`3K3nCo%OvB7{+VlB5VCWvY^XKs)VnZ#ebKQS?#R40%(u3CpD$Ow z!LN#N7DG>Msii592U=gF~x8mbSFVWHTq1t?yi$vfta&X9G zC7CO)L)}qs?q67nw{7I|s`^DuNtX!TA1kONM4&I>PCKr|gqE?~IOb(z$8j(x0acAI z_vnw&pPl-nd@58-!h?kfC-D=jeIb^c$MRFKpFT;E@SPN!EF}cncbgke0$fF#Ot!{Z zLNU|)@6s-_$4cKy*4M)=Tf5CPqvj_8$`<%q zPnRW*7GAc73HLIqGUKNEcu({p7huln>62?;2+yt#tWqG8%*Umv;hy)@cjW-TE;74% z;hX(?0b1rJ@$*loQ?zcbV*9ea*e({-$2RZz7?Wk&+;BH%`_8{CU`Mmm`wS}T_v|(- zzi_twR;$a45@SVF)YE5{Kd%}f)Bf!ZZo4_5Mf{N4awh{ zB#i;Q0<7bn4ygOg6R@U*)f-K^8|)5*KLzSPukQ5Eq*jqDwpnp4*iLI4=@bBgqGlq! z^~PfETdjk}Hgnqq=7Lg{1;TCF-Bm&;x5xZpo$8gGhI3RiGg70uurjX-E1Z#H%s+Sm zE`4je{gy3xccc5NoftNq{mR<E3Bq;$B~+FyyRHtWyKM)%WL=qlE#jA3J;U%k%{ zwh{a_?;af*gGsq+x483jT=Z71D_jM`8MsuLch72pY%C} zUG@t0eJK*d^Ze;AM{~1E6Oj!me6-@hqV_Nldmlk&U4Do)aF;_g0usAPwmgG@2($3o zkjVRvJEBZx;iA;`DSkJVnyhBX`^tTY1K4*S;$@V{q1y}z$`Vf zk%eE;82L1HzLiZx-HCHfT#K>CnT@2i=XrW}eoWH+rHG!LtQNL#XpAh!x0=I8Adt3m zq!27g_bLS4P>o$f7|jjZ5+Ab4Mo(XEVnFX54BQRYX%H2(;(qo<6BpJ>62|N^i z=nz3LTqu)*ZCb~xtpkLMd6fm82qR&z;;=h;BqQo1_e zR!;1dBkMwEr$*W}?)cW#g=*6K#&$=?ti?g7wA5B12Z`j^uJoe`QtzTzEO%ed9}x|G zCFOT~JL^KV>3z|$FGt5bPZH_W5Q240oXwxUNDmxZGOV{i+ZBTW%^W*j;wzLu{h-IwE}FTcDhws^8knVKwb zlyHeIHpy?!ybD=ZWMhgTXks>eFG=(q*}3o)8K^9$TCXO%J4(dari$p%fB#;X`$8Zm z!6bX8zqLMlY#lK>r0BiO^R-`_h&+=XLcj7y#X{$Nh66E+18dRG3|weu2&rhxPL_~a zc%A3aGSfDtmE>DTmt&#^Xif9buXT;?yFyv5N$^8>+YBlzWfr!knWz?tv&{_HkTqkG8b*;FFy<|e@M7&Ql*4n+%l-_?%d4tuGAn&_a;By@wK~i zi%RKGS!+YM{K)h<=u4U85$KjC@H=X#gRk=;X7L?F8{3c`wW)kCduG?u}ZPwR{NN)Ej?Ii9-(|jrhf(qA|Yg$lHJL_=Ti2o(~ z0#T^Af06y(9_|yu zdR9`k4%hq!)x8C5-6f`@PbV?UM_utFkB<^SnPANnxp&awSq9bj4cSNo4FJKKGv9$B z^F%z|f43bqzq&(uY6@HB_F2bCTD+It7vjIhtvZd064hLG0{^b5&L3v^ z3f(4Xi8I#ymSa!2du4ouafuUoH_&m%-^Ie%F^5qa^=EYt750C@7NDqUu)!kUwQ7b- zAa>wEKcRxmkTTDnL+q0HUMcuxo8k@GatY8|v!zClJgnLziPm(cb(>kXKnyH#!5W~K zmlh64qd?AWWmwRIvXO1(%M2lz`?!A7p&w? zNe(@0G}t80ej}XBg36NA5O+a2mVyJ>3#81tCO2i2S^OPYy;^n9klf5+dAl~-^i5#t zKuI0Wlfxb>seoWW${SfYLw+^Bkbc>szLtwQaUrXIhtskRj$@SL>*Gv4W?pFt!esQ! zeP-VG;eRoG8&{?CoaAJjHEAaWJ1#K;r_Q?Q<({2|M#a!r2ZOa)*s<)5!%e!+J8pk6 zd~I_YDt%*h-tV;BIM?yr*qneTWK+dzI3oIx7qW*c~?z;6xB{HspnlsUMe4rSRBz&{qc zc8FpeTvTm-@I8@)Ng?yFg6w9c6lZR7*Ht@_si5aq7Cr6^tkp3wchk5JcP4W!HKO&$ z5TyykrsNpJaS%0~L;e7d3|mBaSDJ_uIq>KxR4F`c(U%;z(_&J#0Sy16hg26t9~#BL zY%H~2GnEsI{q`Kb-8-z^WBPR}!urGF@rjc21L^!QjJ_{ak8XE-`zR_m+CriInB*`o zbMr$351dsj5{Hji`3y}(iIll&D;EN8vgPlT#=LA~aC-?OG8sYDBO0-49Nj&D+vdHGwV zFx{7*586$|((G$abnnpi)JO#zoJ4QYQeq4ocwlI@%xO`2<3tPL6ya1G+8h{krtcso zY`=a~EZi_I(0MLAQY=)D>go)xtQm?Yzh&rcLP^EPsX2HsmR>@%kJB;*(8)Uf!D}lRbH9z1-(MPAun6DhxbMZnS5^l)e@?Zv zMOhr-&CDCDHguY5fUKi(yd-rp-=An9bR=I^oAYvob#=w{NV766n9^oi zsu?ApL;C}werlX{%Gdaip;KHG<0v3 zLMxNI;&i&=G32rcFcy0y(G}df=0r9w1G3KFa$5TH94gFu z)8GG79@Oqmw)_odr}hQAd0X92=ibFAT0GwiJ0SRu*IcG)U|k~e;=;FNhX<>?(M3er zXXXO*M)%8!@VvG_$2h7?xPNM=73tsh=+`m?nV(s^VUV$4Kn&|sjq|tUojPKhqgG;q zXCWF{Z{>ACxS>Vo>j}ZNuA(?t@a~LcusMu+NxgkdN;69{X;|!8>0Q1L51(K zSejx$F{L;h)Vk}8Kw$k>y6?2yZ+oZY-g8raB%rH~ESy!;cIbA^LuzC!@-e}--M=Qs zzLCf7>5b&rzb4%E(k6DBVK_q~cuRjkGj*6b6Z(0_m?xsX?NQ&eBq|CnCQ^K3rEf($Z<(^IL2n*trmvm|`3ZfyCmcK3>5jXqLv_&yYjWCAt5G|=%n{TW{t z=v;4MLfVhVS9Ckw{liAIFZVG&Fmvg*+gk>f!uA?yzpyNQHTsLwk}cR0Jk{t!tK73MwOjT!qQo+wE5jEQyZo z$$h)j_naiW?KFbCU|=1e)o(1XDgFQXkgrr)Ma$G7jjeXG`L#9r_j*Dqkig9ikWzHL z!w;5*--J%d1Ue)K$1b&7IOoQQfg$cQe?_9@*@SO8Y}A(MiuH-^w~Ad)MqW?vR@cXF zaol%e8a2$h5JZGa$#3NCz-Esw-Pc%YJ|IYz7E#Q1fTw7xmLH4pI!)6DX1ks608zHKehiOx%?=6;QG4@wm&UH{LZik1P-m%%eFU9T<&Ze_E|gnb+L()%iYvm>m-D1K|LdO&kGkObSm6TeEba!_cA1IFiw5_kpN; z&I`GzU$#SI?VAsmYG|Zt+vyoi2&fQct=A^hT)n1swuaGb`aaY!dKrc#NP*ZMv+e57 zzI~2GE2%dAS^&svN1W3*+q^`Z?2{q>jk70rGY`8*m>E|&S8A-O)-865RREkLMvqL8 z&)A1L^6+GM4pE~=dyk%1TqwR_n~$td{Y7RmIGTI(P+!hf>qhhMu8&9FiUyJ!WL9}- zYbnbK!TeBI=IB_I;%vusfJ&l)$sd^SbjV!sUhBW1cOf&2C(+1@fzDEf6pefk=-6i| z5l~Zsx}@vPT{7^f-|dcC=}L5LS28;h_H9Zq>RX%4eg_+yB^79v1HQG`4wZKAE6;pX zksSuVun5gjc{|YA6mo|Je!Dj6<6s8enS#pJkL0eJf|&Ud4BGRCn3Ai?Mgza?rqJZv z{5P6ZQn2N``|d^AT*a+#(WzdGS=G^fvo zW6!4EmSQIrn{45YZ8a7_b+pamQ(($d=88e><6%d=b!agf-Sy@y zHJtsX-%g`R-z*#PzgUDYbGdP3m-P+;!4Cqt_c(myefITM^U6G(-(F0Y5o(SDvgiK; zwGr(YL~YiY2Pl|-*|U@KH)baVzi2Dvd3)A&dGsccZMctrs~d9-%X3YZ?702NTo_6l z|56Pwq+5w8tE{-OPVq#5yX!ip;f91VZq-KXY|k4Xp_h{+G#cpERd|G}@t9m&ktbvVd9eK2;x9ep^Bcj$$tbI`0GFw5m}t z|J>r<{Z(oy5&4uTr!+s>*iS!;{8#(Sw%)9rM_M?TBozKcZ(S zSW^%aOKRyHN%t;3-1g4gj>M%frY9&U>I><_f}CY>F@Y#44Q!Y_26y+7D)W6!&tF zPxo`bb+C>cbrjlx2&o;CE6<0F*A&}HUuCyN(EMNvKchYykAsC@Y$UdPzGQBK*&Qy24Z8lM5mTMb2`7?y6DnSm#%N; zXYjfx+CmrX6>v5jya(g46m{D;;lg%j-X=CT=|K#R&q?=P9m2H80?P$NxSY;2!1UV9 zwFBWs|6(Ycj_{%Ixv4{8#;`4nH2N35?PQizLk;`V^O;feKSy}tw)CY=%Z1{;YwSK- zlWWpmsA23zHl$^g{&zu;7j%JbqSws1RIu1o1f7}2y`Dd^$Ad%nWym-J6n- z45&4UsMSICR$YgxI_fUl37qV*@32-O-Mfaf@;N(gLkI!WT*+&`XcjlQn*Q=ge6l$P zMWfSFRDF3CB^Hi0;%2XL6$u+eet)+&&7*_}y`}`u$-)M+An#K`Ogujyv8|XF=Xi@G z&?z%6EQ+VY1WHuX#geCx(` z*rqvizOeLlxv_N)EI@hc83O6xJJ)lJ(<0wuFP+E}iwIO|`(4;tn(`;%#VJxcuT~QEP!(q95A(C{%4K|a!C`uh^}-`EiH*|x}}G?S8*-+n{|q?;L^I8 zOL8l_ZpfRB+{Z4N^F)K@${4A%z=ywli8omj-m)@$=BbMOJ4}rgZrJ(0u*H78J2fhU zV<5sEFik0ul90<0?;D;V&pN1RVuhaFoA=P@a1V{nr+b-wL+S#zAt&xN|4pbqn%bR0 z61PggD68GAU0F+oxWl&>bGs?tv7A|J!efs z`+=MQl7N!8Y_$iiWUi{mV`@gCU)3>dn`_M7KlvCAiZJY^S-KCsv}wSm9iH!4)0yx^#-H_H;vQTtZq5OoTzD_i#r88!Un7m|?}J+suqB z-)@W;wJ#NINK~)1N9u@I0vi88zL{I(}VUM=9E=-WO2(e zf5L`Uc#>`^+ABUp#o&}Q=V+G4qI(buJNW+>xhJ-sI`N)b=|OW`(j#iuyy3A@i)O3F z{1E$m9x0J(IU*kL zST{#(3CyOn9&$7H{H!fA@;RkP=B>GIw5b-6azCgko@Agas`}n^;Evrlve70p{pgPe zy7Of*LJ`1%M8`a5{nD;Oq#q~f3Z5F_#aNJ3KyDqP<#gcCf;fKx_Hgom*;_Y!NTBn5 zQH1b<=0In^cU43DGfO3!s$=2dfzBQF@r^td1($BsiI6P%PuwV?TR`?rP@NKTD{+Y9 zTJhX)a%m6W8R-0xeOE0#5ISMm!6Cc2)G9_oHb5`KGS|Oq4->b$+o2pa=BXhE81(UR zqAKb@2Qq@Qp6}mffB0yiHSGDF@^H}%^I=3DjC|&L9u4*kijtev8vKeX|MvTzaJZc@ zK5A`z;_mB;w$DP6siTdyAH^Gt`}qIsVNzG2c;Q{DHeGn(7BomJ7<%)Kx*o5g^Rh50ksOuqL+%o8)_tGJOE0d zBgdHrO-|*Ci?31crQQnmqzGLPbiTz`XjoFmc-?D_hZ|=HI$z{zG_(j4rX70Ey1Vy0 ztUcUD5fNjFP(qhdRPbbDj zwiAip0)K*EITDJqrlr*64JH_b{A{nQ8w3PG#2<1~)K&84-Z-(A>CYI%Pp4yI1_~2WXDN7X((FMLTG?Ktb$$uo8qrMbg8VMpc;m z2@`JA;~k#o&7x|yCL zC*`0zGg%#%4`;7AiQ*EE)I}dgtKp@3Hz}5bLdW0{mzt}I@F5bCl{-WmsKn^%GsHH!rFvV1NB_(M>>ts6k$(@yG=hLV=Y# zL3aRg7TZ;j{xj1X)+7sU&Z154IhM_e?B@+{0*_gYs|%%$ApV56jqUHn`*^XP=f(?KPnD5ETa3GfJP0qyx}#_azdQsXNjOz;kkCeNLmVGF%^e9e5X5&Ut@Hr^F` z!{^kbKADGjOqiT#0b2Rr6+G_$J*|781IXb?fwRm-JQhPG2=mNydAtMcRj(yEtk&1`O@a8do;*B(JW^q>}pG9EkGr@^t!)v$7>~0X$?F-Y(V+I>}T3JkpPMS+zRWfL> z(Xo9D)iY(Bzh(b``ItF?9(*zA0Q3NRw~840u2{AX$%fT;!5P7yURD7~(72!h&)KwL z*#$eZ3tmh{UI}z4lByGV7Cj;tfd|gX9JnZO>lkL-JLtQ8nf@aJ9oj}R{o@EZVlPTl zQ!@L}!&XslZ+&?wLu?ClzAQX}^K8?Wc-;ofb3qkZqhdtRCO_6LJrK(X90+bs_)N`r z|Lr{C+RPLvyn~GRL8=kafi84`@X^e>6-cD3=_T}vSCxWwJKdp(7Q{!Oy$)iS2%1ZN ztHtpO&^NuCz~F7_nR`iv9_*mp0ES4m1~1Hau_nF#1F!QSwO3dH4g0^(A4?QGI&T)> zUC=KB+{}SVfsRTp0;~4&S>`}vVAZb$paWAEJ4+A90*AfU_Un=gk*zwlP&!U0M(Jj; zGtyHmSj=0s9G>4!z`2AprVn3^AF~7mR&eE0_*LdTZ#5rBYng;>a{ zg0o#E!mAp-sDN}$ zdo@|IH>H;p#seqNYtHzJ9$g~n&|s41wC_Q=6&0@?H6 z_3(t{r{9y^BA~%Q)X%E0OM|uvLSX*(8uw8C72t26GTO()Qi;wir ziamIQZ~AYheyEw3NaqL4X@46}7!7DXmaD+3)2W$z@cd_X%=@Puc$@hPR>xoX>Iohl1PYNggyWyB*clbn ztpgOIl?jzaL^lh`=WB_5arv~Ftm*+%il~j7yv6Rs3b1<_!`VB*QnDDvN>DM%^_kyv ziHfC0Lcw6{ECu7(&zfbfo)JXHeb%`(XHrK2^Zzpzc{Vki7R*#!>GK3f=ruRc&S(8* zrS*7FIhzv2Nx9>HEjCm8RAIkbVa>$PkCxoBktt6(Q7&Bnf=%w^snXJi1O(2dM6Qi zHhn7oKVKyRU#|?^EHh60V&CTWWY1U_c14St-&5Hy+va4;IgODI6YkrK7ZZjR7nd9B zvf2bNRC*7a_e|1 ze^jAu+Ksj+w+(8T@g_3sG3wl1n$9V(;A(v*w&3DMnC&D3yZ9zACw-hII0#sd3PKi& zM&3!+)^)w%!&UmLb@Am_CZhfu8S;lk+nNdf6;9=HcMss`|39id`0;>saJ+1j{0Mr@ z26D9adkzCPVa_~2z+TUZKvG$DC~v=i0f+Bw<|*pZy`&h}9iscuA02n05R!GmHq4RG zM2buQdffED!gDgsa}H#jIk=1rq)u{eCGMr;i>D)nkFuP@6xgJRut_iu3HL*X?7qTP z&%y`Rp=r=jqQCm5I^q0F9|F)RoFs-Gyo39FnK(-=}M zHwANw`Iuj1G5Y3a1yopY17>(21|Enfq|pR)2v)W72Z7I%tM;wc(!wo<);{IaA^?#K zwqpO7XAhoLkss+c%&GEuant1&^iulmz=PM#&Pux_k@o)c)D8yt6t`snCsu1 zZEMCj`I&g+uV@iD*gmxwOUVX*Jn-PLICVW(IS?o15>zDx20-D*3Ld9yUV zB^ud^SFFIBD~D6YiUvP@IkwEUh6`DtwbME`FL_HZ0}qbC5i6S<&A=O&sws82<&+Sqpe&IsAHkyY zY<7kw2ea56XID<~R#fJ?_K!gaY%Eh4z>?t9_s(Zk_2$d8cY_32$8Os+QC@B(@;N#TzFY3AWSN*RpQD0C@N#p_a`B|dAZ2=SKF}=n z8W~L^vG8}2X(SD*%j~%l)tSi5uk5mO*t@p3W+(AOhKcMX$XB0&kklu$bNwb@GqJ+Y zVmJIh93dxaqwAu1C%4R@PqFjfbOLd>V}wqnv9Y&?c-9_@q{?GHIaDvxJ-AV_RvS;B zj{QON`jt}RP6*1kn>d96QDz^4;a%=Bi4!Tqhk}@>WgWLz+>LzZdI5OkgZc@?8RntT zh#^zl@^T?ijs9Q?Hu}PDHH3aghk@b$Q3$isXWp2lkTKqT@Lo3l6?cC$u(!eSr!Wqk z5Ukxi$&Hm`*z3$qA7QJ-Kahi_Ah!0$>acLU-9G6vL55&se@Q!LenDgssksD~+T8{0 zN;QJ)AU?XKb@*ob&8SK26R0kRZk*6mnm^0Q{#FYwGx_u~Cp@oGX6F`qiSU_-!9I+- z|2FmsbNOsyM0SnLHYBwx>PXM7_=TWXg|kD^H{&zEjHPdVPan9XN86il`(xqXNeosD z*-x;IHvi)D8Pw|x%KUzgxY^sPcG*v`twfxn;M}U>Uk2xPz4*mC2pGyG(Qv`tB6GFE z2+6i-u*95vmZ*SryCrCB>UKGdrhg!HSC{vdmC|lA7XW8CYJk>jh9f~p5;BlE+oeJ+ z+vU%w^4c$(6K*1?W#g_@sQ!2^jZ^*kzkf_@6A9xYIM0*MM1?MLrRyy8h{%8$}bWXJU5@w}HBQ}~ZzpEWykMVn~G?VC>f>Z`NU!XuN z^y|C%UV#r`UACFap8aIt{lItZ>Uck}ge5DExoJgd^t=tJit&-u2uXV#Uyo3hjJ8!x z@|cc}9Z|&|>9jnt_{-3`SOvfz`5p)|p<|pM24?It(Xo9ADBo+2dsZ$=5I7lDx9n>4 z?UKT_$%~9K$sU>tfc|@+BVzma8}$!@UU1rlgKd@YolpW3NZ!tDrbr0jc{0D0ey0$1CD1tu z>=4Mlf^b*F07s3EZuv9RcNiDH<3eU+ zO$7=RZ69A1N#Xz7{TT^bL>Wfyk)Y<;!nA*LZxz9jyDzL4(*gU2JqHeobFZ`1Cc%$5 z^G5e?5S<>6Di!IDB}`Uq!5`6{b;#%$jz@b|{Xi{PYwLNJA&c~obHAUU{lD}Y>}^BDV;B|DAbt02_caRTuTZUwYw7u^=^nUL&% zpCk9<&>2r&=h>)BrjL+fW)y3eM)FTe<@dZSp__tYx8Lr!M;CF_`EoG&R4reTdYO_- zlr5sMTKB-QnsrQ|S-ZeQxS#G~vwgwcgUdyygjO;XvB4Nc4s0cMo)lPkrK*VJQ;Qt; zNGCGAB!x+;Cl?ZZQS+YkhYzr;vT&=t0CPR_ok7Sg3j3_}4w~ z>@Ys-1kP->6TRj#x`0R<`6?15=~)Uky@~9+ibVE0FV=d|zgoM~E?}YK=5~H%$e769 zbYE2AT}@0JVn5!1H%xNEckBY-DZ<{bvrofb_I&u9b_~ZkW`{H8 z30+cLB1_A6xLXT*UeG+llCm&aKmf4xu>=*aw3W;G)6t{Dcr-jWqZGoWMrC3%1&M^} z20l&dM93-xMdGQS)uG#FXc^Z*6P{ZDI)09X@esUQFBfb?JtZ+_SZ4eAI1_V-Xv=(z zL!ak#j;W0zj>-#tkoS4fw-eUy3;yTqTX$8h1^_su(#btZ_f@!8&WpW>YRoNXU=MHIcz7WmwlPsE5eHKETK5?ic}S$-&lG`yZ~Qb)&paVX zwKtN7E!e%LvRBuDRk)aP?yQC|-5GsE-$nAC)mJ<>zv`4i!-3p?^9H_8jVq&4G#ndQ zlE%^CA3IY)Q8V^AVddf70Sl$?n$DGmDnM;7YB=@umj+v^SJ?ZLE!nm2(E*@ z4|H6Nq%@M38I3nQQ4o>uV)cW8Bta07XX%TiAZviXq7E+@qfO_jyhEGuI<*J-`BhH(EZCvO zGe1=V`CS@6bv9eh=LPO`RuH4vV@@7hNt)GIV{;F7^rB|M*WpU7N@R!k8GGLa#vb85RXk@BP~k2Y%nxTQ2sQ&THE0p8E8NEvK$#> zwO5yQ*2kI&j(;J9ry-be#uo@@W_^wHN7-Tdcd*aGPW+3O7UV`!#*dv6t+6^ox$8^W zrH_rS=&-)Zp=dH4Ai6;Ll0(8b)h#Y3>=jACYIhJR<}5dgBI2@o*wj|v#?L86U@+9*~N26YA|WJV_tD=4j~(V(&wk;;sXuOWm^Vj2Dn zw!5~qTf5@cZtd2sw%a07m4rJ&0&1-&Rq;|iakPSMF?h@S{hc!tg6;16_EX8s{LlYf zp7WgNJojgWDtUOJnmeizDil^zgE4GN!5@%qD@cs-ysn=PfL*H(GC79*JHyti@#cow z02<@4SKo&uszOPt0jYG#RpG$mpqGx)I_eWTt12&cuL%VssaT(+j$BkVBAnvDnJn(l@9V^h0a|)gy3*xX<;A zd*xd7&ovZ`5UTA@F*ZyiPFcI95tNBswW#;DDjPvv#9VQ<0?QM}`gM^|~R4VoV;+&B}B+~-ArEiV^+$Fa} zl1s|+;&a3Pv@kKqvo4bf-nt zk22Uoq1&4)NWid4c^3SCP-(!Px!>}#&K#@;nS*RKoKUiwjqbBaSq^v|?X*Zv9-5d= zO^-4Pi<4KH)04g=AJl6-o+K)b5niAqUcocrf@kDQLaEP|Ol_bcC{L&!PUiAy=b8`T z!8j5%GN_ql_FP`hxg^<4k0ISDFubZH)OP2&dFV6!X{Bw=d>U>G0)qY9mtT6WxDu!V z@he|y+LguPqwYPciE>24YgDuet+g#Qh^XMUvqU{VpibV;F6j3 zem*cFF;jjtRUJ|uMCB7AG(6c^h8?U8lDP~2->0LghW&nBIFt$&0-`c=&F=)0T+jU* z@$!NTeppbL0{|?Eadx~iOJsEFJ{KR=fcg)X{5&KJc8xoW!Vy4*;1p3b#jA@ALnBlr z)OF6CUmOAwH}%*|`{VUFP5#uHhCT{~7nE@A*bMZTEE4b3ie!Jro7_Z0yp@0DlgR&h zVtzmUzI?*Tx~qtfRWk0tWqTbv_&~xRQX@>0#3_DG;&}SfQbNrl$vgV;kX37H5+x9w zco7P~no!Y_0+}Dlk|&xLO`80~A9&TVw9H%)B@#Eut4P>vK>-Pwhxpsl;p+Y$et5`x z1n<9^>18dqMEq8mgd58rFQcuw{tV?tQmS8;m2MoKsj%?@aMERF>S<&SOp$}4u7dIq z9E)j4ZbVO~t|e8%n-TxoGU-nKSnnK==SOry>uPj!>fKHM$xn-yj0kn@qYA8@VH_jF zFj7w-FUw~ll&2i@4m7ZhPvpr?7jHA^ayGR|NmbHgQrHM1>503w!=pg(MZ;;= zyMcc=-~*aR0eO)?hoXp$62V4SQ1{u{1$QYVL*w$snupdH|@;9!)jd!T19eGB&%{b?B5PiQ++qMDDqlb z{)yD;y(GBw+IKOdnqfK02u>5v-!7&~$bCO}L~ZtI)$u!Prh#VLTCb0#r_CqJ7VpNR zHB*aHQ;WAwtzb();61Gu#C(=M1T*_3esM`8dEFwq8KTe0>zZ>PnL%;`$7xxc-U@uJ zsAVUrK&W6vIB;pO<=^451^8~W9aK*Z1FblD{0H7#SpsWkRTFg;BrRB8ca9+z1VAh; zr5V}4I?l0Q?frm3SC>T4T@p@SWmuOZ>98JNFoEYI@7N*9*fn3T_BuQk1aB7e?;xYa z*NaHUh`B3Sou9ytvL{kAzbNtNn7_CpyyE7ni2s-5MJSj49B2C%mWH(PI5o)fHI!-& zkyNdMGG)*iN2eh+x5^gL*cNM2Jqu`qdGTZjjek2Z{afO{wV`vYYI2vE!qLjyN}$Y~ z#I3>VTaVK4GfT{ONo+N^+3^ho^{lw6YD?Ww`Qhc)j@()&$$&1WL9FVk!joT4j7tXc z6E^6LzIU0eJ3dp&%U{^8N7MC4U4J`Ffc@6DxF1}8>&RGYDKEcAkk0a(t5_GWA}2{5 z&?vyp7hnhY{*8lYlti2y{Qa9VnQ_hHiuf>%7=KeF$4=xTCX#v3CE$4Ae~Dp`G%4b= z@@jX}D)EYVT!k{+Me7jmJ=O7MZ_U-vkemb!)7H%m`zYO-uS@^r9`J5uuzE>V;`Ae% znoD3f@w8985UEL2B%aAOw@b3?zy+-1M9trKb48*Fy0s#9*2Y4_U4{Z1YQ1n@X!7pF z9ii&hs>EXaohw3^V=8A?AyPMOB}W}{^k>n6Z4vL0XZVlBys#v8l z-{1x!e!@XHHw#f12WK~z%(sQ>MRH@hXLE45Qw#?u<3+z!=fKW1uz}cpz@jO}tFW7p zoV7gCv^!cb2Brk}e&I>Pyu44=!R*Gcvytr15{)o{HGQ}z0#P_|d$h6^ZY;N^ZHg~h z)xcPGP~!d2MTIH2;L~?*Z*(gUT(FUW3!ot+u1})g_ia!KD+{i0E9g~HTRYzp>lexu z+LB$jq9gtv^GSEIT00-zK=n;6<#0FfEPOxnPYlyxl9J+8i;_}cZ(~KdcnE>wj&OBBVpOzX&%uJW#My9V znL0F&PHmk*reu z*cpTa2bTUK;)OqsrY`0xNdA4CgJc97>y@pdW^Y6I;*e+nBfUs5tkQ7)=@O2wfLvcBy}P#80;AwGAl1xD>e6gXa6h=1@XnRHD*7teF;R zyy9$gX*A7~QD)Ic}S^Qkm7RFf&R*4<3-7vE{*-N*Ik^3`Vu7{KWBJV`wt+ZrMmSGrR(z}@MYR(!0Z2ollTyyDX$WECNT^vS6Aj#kWIZG+%P0Q6cvfzb#Oyr zd<1W)WkFXc&=pJH%2s!y!t66ULXhwDHyhCon9HZ=VBWo&cBYQcC_UHeixQZ;F*ZMs zn_9~z4palghx7edKFAkFbg^Ua9Yc|knuDT?zs@&hWTca~016$*)~GMemPK>7xo|4R z%+xS7GPit&_N8uqtmgd0D7*c=J#@rNIo%3rL|e|M>84+!fZJob|2?$F!j(H1Su&|S!j{n?*6p9?~Z5FHXU4||7@ zV~@L`3Q?nCK>M6E*F$1ck4UXx#=Z?%MXxm>no1y_%|QNUK{D!_Q~t|6g4*mdU*-Zm zEQK5?X2=FQI9Kms9&g#Z`xt~1mu_nYTk4zki@XOR-PN(Ck8*M^&IBZ|;l~QB7KBGS z%1$74%zxj?|N5uVeElu%@IAVP-{sh|Y#mAYWxvc8@=(>|uV>Q((+#8DJ zR+62@>}xdtEOAV-FQ04=bLRE_h{sX?z0A}|(=TQFprApy%0rVho0-v7)O+OpXu+Q& zU4I%GDKNUaxN~(;4c($4?C2w-f%jU6TB;`7ULE~}({Ru9H6MZFW~3>}7>lGOpa0>j zqP1hT?~{tUp^LnH~4*C_|yI^)M>G&VE%p_$nk%?i{%Hae<6cYe95vq z8~gd5h}Yaa@Ag~qFB8*U&7r|DW1I%AHVx;oz&;mh4Z7Q80;o4Q{JYGi62;l=G86yP z;|Eb^dp z=*nK(f~95hh#-A?I#?PTKfjdF_!F&1+#&PuB+_H06F1n6S`=%tX-lLCX(WY(IfYqf zrhvJ(Y{2h;F8VTO1s<`$JHrtaw6GQ_Bcu_(GS(|;~a4WAU? z|ERgNOlfE^yJ|%jU7%;X%#io-8sD233Dt$xlPhqD|Ca%&XAW$pyKil^Ya17$ynyn% z$fgE7Lczc6U1M<-JzKB;k!Y~U0{XY~bktmj%xj35e4dj-E0);9PvTD7c(FD;<1J#y@2kmE@`0Ev<%b4fD)vxkLI5n(H8-jFhbfq~ITS%z0tpRB!rO z?u8&P3~FJXdMJCUy^hmuUr*S){-V6w7)sd=bkwt4gIETTE>iHZ^4-5_9^S%PX(A=v zi!g#-vQsS~dN8sNyL|s>ei|6hy=Imi**s;Ni?TryQ1SNV$Itce$x;kQTI$pD3ds{Q z4@Y-pqu=j(R%I(jQZz!NAu!Zh{$HVwi?GL|9CA z$_30+^ZwRsu8M)GOozc|9$+Ys{`vGB`MhwU|>bL!M6=(22+MS;U5(Buko z_?~JwLQm^F0?9Frrj?VCzJgS=WOKtd6&FbZ88~K2@NDj^64inO*$1q^_7*8oHBzjp zLk9x~24VA?#qNB^{==Q`dUw80Ur*|Z->O~Me~T2`tt4SuX}iLL?ffRqc!&@d6F?ab z{8UX__OkaHPRThM0tUihfB}Q6PWN)K=v7T#7iU-E5vR8+ToC{&O5`pLE zGBa+1J-aY7Z=Nf)=e+Vcs>pFF9mr-m^QYP5EUH%w^UF_jo>BnKXS^L$r_Bi3bK+zC z+4x@U?B8I9+Q%w89}N2_de{ILOsH;U+`-4Z%1b}2^$S(0y%G$L4bn(vUg zF8f)TT#o0fJ^E6&mXTyXf@%+CnrIetXf(wyYTob`4rb_`*%_3-F8dv@wQz@c#0LP) z=rV4vMm{X!H@zI{zoNo*uA7-#R6la4)LwSjHvZBjcQxQ(LqP=j zCv&L8Tr~XoP`{Is&cRwx^Q7>>Tcx^S5&y^g>};I_lP1ho+Z^i~D{78idN$dhv)wz3 zqK<2>Yi-`&-b2c8^8nXmx87J0umiMoT963cB9jjGl9J5rtuogvXSyoggiHbUU}xxW zD8rY=3%??3UNG9f1p`Iw@UFFA!rLkvlaXSI`Ei_Dlx$_E*>=3Jr@ed+T@q}Bwqi@F zN_PJH9EJP)B#qA2QY85ieZ#!+G9Y+}3RR%$*zc37Lg1U!X_|hfGXUrD4V#B)mwBtt zQQP>%+@*2yG|eo{zMdXC{qTF0ym!R26AnM~c$OJ|#M4ji85r>Z#~c=|*4}6fL?T!Q z&OcmQRW?V6m5ZF*+q8iS9%54wBvfQ6ROUBOvht0>Xu+J))UmZT`v&e6Bh5|kGHot< zVFzSX(csu7Aez;}X-*}1yubaf5E!j1cMefBnAuZTRirL0A%Ugz+-?qk#~Y}ghVIY9 ze0uWTMJi(+lG7U;_y>`6AXDRAkN}E{IR*^vRKdB>shAWsTFkoUC`m#hv^{!JHWI(dH zQ-vEQ&gNBXGzM#`Wl<+>Cn*^T@Unbt`bu_ka$fwXwVZ{#$aDLmBl46_kg``hT8PJS z$`K~m7)pIf7a@XOc+%cU2kE*|cCn{;P5DP+DyE0_*d_)+eokM861}U`T*W%|o*f*X z@NX^8^okK&%Mo0v4$=%v?$i<_rsN$(lmV&9_hR|H3x*Ql^Uj58=(Ur|HqYN5PS4M; z#IL@83zs!ZZHzW;;u5up00u+~kO}acz#d~>*NW3Bd2wk4?5TN{?*kLze!ybh(xruA zR@9Bl|KC%>ds*XFJ6>sLEiA`z&uf2>A42}D@;)t9w#w>)jWjO94w2T8mj9^e zlYE&iy@@{ew%=EYHG7Mr`zxM~9JM+6+-uQ}t?|PCEtz4+e^c`Dd*T;t}P{ zgk|2k*itzw1m<*HU?LGk-m3G#mr&~3!ur&eCH1Lq7I9es&GOz;ghwFZ?1?%Rj*bwg zo~pDyfB#G1kj|%-7fOVI&R!GZB-Z;yGUZ`0KR~|UbYW0BQfaF=;OU9qss8G@@rkNu zf`uYJq2}7E)>|U!`l65@nuE1C^oc&;Zj%l&47>OYAeiSk11C_|#YbuIlIhI#EqWiSrz2X3I zJt?>J1+&gjGo(;7W@hR5xr)vodeb{PN+dPFN6Gx#+e#fwS=(X-&LQB*W(GhRB~n)T z)nMvtCLFk?xP@8*@8P>&NisIHN`WMpVzFWJn*1e&v2@7br!?9R?W@U;54SR9W(b9F0xyKUnIC~^WeR3Yte5(8 zDMRrx`?C{mT4EO#7~W->mZr-Y67)) zng@64`nQ29lXdf?hn^zU6=He&pEV10Iz^#Z-Rpf8~CDkPl)fHJw2d&lUMKZ&=L%=n5_uZ_&EI1S_b5+tn8eR=dek)do`~p#^_Y27x=$`G$|EqX^~)$# z06qR%J5AMEVfJQZIWC#%SOIJV>n=PKA1H9`)}j^;+r!RFfnKIFT(82aEu<6yiCP{h~Yndt27~7uCE%th1_c#`RYyGMiqzXy7RgoV?DmO%)!JF<)h0QRr zpiuhq`yKyg$wB_KuuvCJHJVfhpXXp8RY`QJQ z>2LuaJReK#L$Q{=5E{#-je1N;uugB491@~C0TjwsM{%wwj)B3l;oCDss?MoMsMZEAa1M%(|o)* z`Eg-l&;lwK&-@u}YLt?FP+LMyokNMkUEzfy3_w||{qacy8<;__M?r>03C8Q7oW zNLluxkav$A38@*?BD8GhY6~@stwdI#g>ktFP9)tsZ0cV^;^yFN_o=$j0tFB}VaxvN zvYKPILo>J%;$yRQ*i&yQkgwaEJOMXlYD5nm!-tR+^`Ey8B@2J(SNZ0^E20|5s7B~6 zvr;cD+a%;0@I|n7J9Lk`ASlkPaRLpj3XX{N_Dqu$5p{mheEU^hJ(t1=f<69M3H*^K zEyA46Ev#aL`T46*XPVj;F#eS!ZIvr{Q!PYwo8#5%ODQZ@Tb#PASaHxMKb9Jix}zwu z7ab6nS;TS|_7bx)*kIOT*}HjmJBHqNd_i7h5*7>>wi)v&M9s`Tih=_XSifAN3n@=z zh!Cjf>z@Sz&YW1y|0zunbuz+6xalCK$KHUX-rLXV;!v=ZMb@wF>mQ4#fnYi*yUdTY zIxZ^g89Ez+0%-|Nq#uf%JrCrJa4CV64njtdMYW`Z;9(mt5SCG1<*m|3(KC@tu{$kd zf`K72R0|~-`6sA3MdJC5pxM~yZX^;q>Op) zO$E%opSUdhn;G|CLW7}R`}@GpDvct(^V>3e%kWc&P~U{2I;7-OuhK*jY(1u&ZY-~4 z*3hW;Lp`ASWZK#I#9m5Cqw1SzX*Qd0Y|TO^fnQSAQ4!ctuNY=jaJOe3zX=X)7Dw3& zUGu8}h}BG>co8#phDHLoWaWL5bG(#TfmPUG0a}*mhzIQ~qB0fEk4O7Om61Y1PL~W1 zGJGUXr$7>iUsY-wX9#?GPZ+tJ~%#z&b2FtE10NY|%@ zj|cgI+-IT>W#%#IevLZIX6EYQ?LAuBtOrO_=FuGJ3u^MI7N&A+X*Qa z)%e{@15~5OT=BZFV5O_5HEE|gA2W(nKJpiVn&?Chq9!@s^K&8rN~FnL@`P-w?x%mC zKCsDaR|ccdiad(=dhN<#0}DK5QGC*rrudhpEKH23t^S!@7ZayVx$f|nu~Qb(#(^Q^r)X)LIvojcAJ^yQk9O5aM_jdVK-}YG;+_@>E(2g zw`Ka7`KJ3tEpUf<=46oE@N+Ev#p9EN&DLCBniwPQ*y>QyW2fSi+xrL&9El*z=Uq{W zkpD*$`cOc*+e+pfQy$eRJ4$slurod$LH5 zdmVDn9>fSH8{b$2Buo>G#`hlu#pNV;3MzuMf%=;J<6QSa{axPK^?&X37{G60@pKa3%N-!QUn zl)87?RVpdje}>oIYG14I+TY|!vj3uZL$ZImw`$`^eT#T^uj7L<(Mb?#rmgwd67<;? ztPy~Q9l-Dthg)opa&vJ|EW?4W!+#+|IN_2YDP5}8iRJUw&z!0Smi2dekJL{Lr^EUA zIyXx$!q&L`#+v%7mUBogglAcr+)@Y!BsA}m!S z58xOzLv7?o&0Hf~hUS1isL+_8Z~b&*H9y?OXL)CiU5}E|Y&nhOE#HT%wJpgXk~k*f zFYDxaG_Wz^)o$Ao8lN9}ypWlPtLyU<_{*T0BKZ(mN;*L&yA;!Tq5MQB_zXY4%inFr zP)@D)CN7<@_f##ZJnD7HmZ-+)< zhX;2V`Eo|?z4|C^_vPN*J1t(!@X`zHhf65($3V&cOS2{5SN&&^zSetpKfs4|$7e^= zjzY$a`Rx=PiNolk&Daj{xAOx=@)FnqVwjaQR8+NgH2V;M?m*y3Hym#k@>Iz+vx!8Z z21h0mO-1JQ4znxRVP3LB!r+35J%f6+IGF32UQfa_G|oOJ(3zdGC;k=;uVR(w95I*d zy1MO>UJo!AXh*mC&0JARhlRNXA()?T)~g9C>o(t|N$&zcssvS7tIBfcMn0@^GX_!( zl)w?0YP77K`A?D;h(>XkZm9Ln7 zYCEbhJ!;jXZQh#B1%q?^bA((BqfUpT&FE-&3PLk>{#5HA@13_2zXE zO+w>+e=>lwfSM+1|>7mIPkots*lbzg+9J4`GLr{f*=-;n84xH77C7V6^mm38({M zg_|#!Kh$t&6_&8}hb##P_`9#81Fv1yNPkzd_Xe+BvHi*3W4!i={V>XFpJ+djSQ2Lg z9`WJ(yv&DqGKv>QDAHJH+G-^$CVM}38<#(Mg z3xl(mtMVxk+m+80xwxR`P}Z1#R}qKXxpLI|;m$s?fb&*wm~{aQSmF&s2(6HoXk%tf zxcUSfPy0K=)uoBP$_>&SUTjZHdJe7YAvzn){rR6e%$Q+y#JYc2xnbQYOzIibgq`^K zV5i0L#ztCn_dcr`F`gx*JIq@o4(~mO-|TmbBp(JL&8OI(WaG=8+1c}*p}SJ@zin8`;$tpP^6a3O?4OpeU#0yEY53u4rIK;zX_XEFD>$ z&H2#(_%nVrd{{8^Ot|{^K``jDFt8+jxvW>x?i0Rhb9wUWA`?N)rt4t0dTK^p;I$)b z66X~u1qt}HHjfRsG1M`Yc({k`6n2|tJ4<&rs*gKB)jms9f5S*2P_Wu znAtp!B-L*uxqwS1@epSpNbcwzFRahwCbaWb{RQ#{<)|TD6FWzj$C#hdQ}5TYy#@~X zU7X9Hw@Q4u(=q$K$-c5B=PSG;tE()oFt61MQHk79*~j=;Ez!>SxqTy8w%;{rd!~@N z215s@4k?SD9>MU`pafBuGakc^Im^5-T!p{rL6A@}^q@2~n=2k?U|a?HG1zjKG%@pm zgl57O&g>~86u*t$!f5o!U!M-<7ixDyDJTk`w4z-SOjYeb$n^{oae6SWk1RWqoYabiAUa+FDc8;~q41(LnPH7U0V9Sbbm&*!u>W=65pGt#e?@MRwwI9XVNxlyORq;~$!Owko zQ?yl&zMwd^em*>H_gcw}cIfN_qJ|<}yzO=@#XB_Uben%=p`^ttne;5{4o3p-kUb}z z%Np@~Xss?U^V4@E(m575ONKr`ercs!C2On88oKra-1M8>Z>I3rz1>|4Sc3oX;0Ai*b_|9%sZ6mVt_u{ zP>Mw_Yn!%D(t?m5LXVg5#9m%v(|`IOqPHyD?#fp{xE^x|AX&=sd2Xr1ymPL@-Kh(> zso@yxso7y3xm>8sY}U~?Vozs-D8(Y&y7|rPxK11`5B@_r-Us$9?F>O#z_)L0w;PrhX8HJ_O9yYp@6m$AhFE4yAREn3 zS#Ch5D$CP|fnE1Cy|RV-3al2t)4K%8l6$3Em+ZM10ER{iLYTsiPmJ_~V|>$lwIt8# zuja=iVYJSoGb}VzwC+oE)O(J7c{Z~CbBVEVHANhQ*KR{PsTju@I8je+w{lkSrxG2u ztt9gczkXsUuw%)w{tQ(2J17{%?OxE8`4@l2f)%yrTb6+J1ee3Dg*APp1_0bY9}07% z+L&TSonH-A({Pg|LLh!z+vNKt1Vu}bP{aPziR;EQzs$*|@&F`#YaF_JDN(}P02A?Z{^6x^qr+$O2-lOCe@@tcov3yVegIw@6?f#`D4v=Z zyd|zOm+mO*8N~N(Oa(RoZDy5{5}g{t9CIQ8@M+jTDMAl5ZK=(>4m(N8ucDCy!K;j2;QjXK>Frif7G9?%gII6XMXf`=}Dh=ClWQip`;U)U2Wku z%kX?X`yKvEJi7UsWE{p#kVJKOh-GTjaOFPjCPyS+4(nVryUc}O5^28Hmb55|G}XDx znJb3d(i4u1ldZurdb>=VLq$KyC#-R*6f;AoUC25mL1s1T@ZGbOK!R90>TOQdzS@tk z+mASi^q3d;IM~4gP0?aJyap#zviK$4Bv+ICD+Y{LcybZ$Y1QqAYO7I6dL7S0$ivll zoIffE}Pd%?2?Ye^rE(wZRWzn zNI|WNQ<-^zg=mXZE#flrCbe*<9mk@w6en7^Wiya&W=|%PKnomVzJ(w3rs zZowU&*rh6ZwP}nGaJ1(0-<;IL?PqN~$ zkDuKkZ`BX&T;78`O%H|x`@D9sF)GwG>Dj5?ngtaVfp@(2dHhD=njn8h#z%T<9=0Dd z!;_zm40&O!2}2VjE~%MSL6C2_7hkl>ZRBNU_V$BS@avb=`~u5bK`)`eHt`AUcKixa z-}KzVd`@}2WYI}yiEv#3x)BsV>nu>BhaxUVBlalex~1N{(D-^Hsp?6WlOEf5=8(LZ zelf^Soz|mN>!^F(arU)6xkao!b|_2H04&5FhHq*JD>4)&$ z9~UN0$$@TCXZrGDd)nOM{(F{LU#*$)zZHa<Ip| zw{t}SI|%DiW`2(9A{%dVZq$04M3b91(l{B}j9Y`27_NTMMm(Tm0U)Y&kB&z&f@c3K zCNQvGZ_PDP-v}G;bJ%!P^5c;zw8;$igsbNfc==^EN@tC&t3{-q>40E2s^<&Y>o)L5 zC92L|w@Os~9afW=SnWEEPv9t&WXP#=eWBN$PhWg0@Y*qCFe7r)1U4n!aCK}MON2yd zGTja2$kRMVqLjV2>T5hreq7+Ky25^-KhnDK3-!D%=0nb`8TJ3@t!tck%;Hly802UI zn53`0#af`uO=!c3Le#Lx@1wnS*G)XuODc z>J)UhCxQKmuvfvM;pOvG+~Yx;8kRJA-*Cd}a8h53X{GNTcsG76e^3D$#U zXGnWwcxg_r(#SI!|R3IFaxt-Ms76Kc6z&w@Fh;an+hrtUAuW1CYL?Y z;t3>QWJfPx^vp58)6SC%$KBeO*Xh8z9;B|^Wgef7qukyw84TsC>lOj`TxK?%uF2O; zK@4l73o5wXjs=~tUTO^B69j#IrU=5r=c@jvQPd zI-(KN!NcVxOg~gpo9DGZz|-n*`DKYQREHz*fxV@6+k=b`@G;ZxGP{@zp22XI0CQ2S z=JH6K>#-gp^A(c7Yu&Cyj7Pf=%;Tqkekx zTD&LycR1yA8z<%e<&9p_I&b~P(iQ~)wD_jpe;wffBEEi1aVYR?ONqS|NL(@;GtasC z8E*4W-ec1m!vWLsBbm$d zfQn)^7NHKY-VQCAL|msp0HZL%rHvd~KX2VRu485n|3|+(n%3w78qIO!L7&tKOdI0l zTH>Bg9ADjT2}puX{JlWh8Adi8kJ`&ybMy59lc?|>ySWjaw%LoOKd>;|Iy|)y|BBW~ zw*7b1{yp>*IZ89=!aIr!FH2&ynro!*m<7%5Ssx_|EZ4aWFoD3J_1X?B_C^mJXlJ zhOS2ozr_m6Ox^BglqpgCV)`{98;Os9ZGFK7-83&kxE))b(3e2$3dX3Y)noqXgNNDl~_>>nY6Y8z{6F*9=Wgohz zav}}u{R93>Q3ZHFGyO5+Oywu?ArO0ou1^{pb40elIV2jwE&)-XS=~lys8!(6T zvEVh~g4d(TtwY4Ot*qxbiko`o_#q`(h~2+j>#*(}LF1lAjhP&dcf)99d1z z&ffLr*JTjYQ4$`kNc)4`B9QaS=h_lOhqeidiflXgF6QmX%s~b*5^0rm=aMc7qFj?`su;!Um=jbcV_?Yvwxvn=DObtwJuPc znDAdhm)EhBP}AHpZ-8-V!B2DWy zI&Qn7@&f!9Lr| zGrz8(mYb5>GgaFV&Y#)fU)ew>%jtx{=Jj_OZ&SOdR_Z+8s{cS(qF_{e$UKLj}*1Z6vU} z<$ckt7CGu^!SZNY={RO|dFD2{5=L&L-xb6KyNh+j#^oH}u>XdL&2An|dbZ}+`li1r zDWYf|%~sQw=C+5w-l_~gT69F2fq=gmj=$ScA zuHBtDHtH9i4X4WiXNJOgYL?VEvqk)L3+70=@G4T&gxY6qtz_KQw819t)o#cxNlNoY z;Z*s(nbniF3ye9qg@NS7IdH@RhrEto@-A6xm}l&ZMJ9yCT{CrdV>C!#MD4z#|A^T{ z0xvEZL6M9*h-b)nQeB}ZVmc7aVoP8hILPsVA~IN}(3Gq=TFi=|wmNnZYT`W(xbigC zPd%iIz99*80L(jCe6PJ81kEr`{utN7feA}O>`d=Gcy?Xj(WXrZzW+T}zKX?EK?rX{ z_$D8F|402!Sg1Q$(ZY)aT}`KV5kpuE%7SYJwlCWvoIVdi>QINvOpNItBB{uH(pcf& zi;4$fJ6?VrfZa_$=A^PlkfB|6zqR}gAwkzp-k+E$e(I7~ zO(>EGM@g-7F&yDnuL=3PQFzw|_Owopp_NBnLXLlfRH?Ao_L!Y>dJ0J0R1nEMQ$urr&{ny z_%L`KKW8{tVzChTZ5?MVr`nhtGzH^OdU_%JiA3KDJF@l9FEh*2idzF4w|MPep>HaJ z#7IJ630WU~m`i(Di??R+`RGd;Zi3xIY++QnY`4%^nYo?;;VlCD6UW)7o7T12vAgZl zFi)jpdUGa*GSNza=wI8QEmbpWKQ6o`oNndrmI3mMyj2`5NvYmk|s%v`(jQxI{#Hc6bFEm|-hw=@Ba)Ha6!c0#v!=QY#MM{)`D z(q&8Q*Rr^fTQ$WQBvpjc#iwF#1O~;0j>a+lZu3kpieZ%Jn}!gR)51?MD~za@9Z&h7 zT^wYuMP!#(;>RJ~nHNjpWK$1Y7TL*SUlj(lu^w|FZir$=o81OxrA~Gc@Ui{o$74C_ zDpn()qS!m2zBnz~2yOgA@QWvQOTR%|>0^$bp*9Z7rH#-bl(1pX9D)=y#0dchgA?20 zRlu_5Zb3P5I%g(*+3{Mkh*O>D6f{%)y9jDCnj!dV{!P=uD^IyCZ1-GgI$U0dY^@lG zL|=xI%R_3cktnZ*DhGfRS6zt%8OXU~8|2!(mKW?jXzqAgM`(L(;6OaADGJTiQY}c{ z)pB&CX;%3>W&fyrdiwI>e7rmC_xAEnvMoj_&ZB8Vq;i{{s}H>^sQ_9QL!ks|nipDo z0Y46A{KAP(v>FY(ST^W+c1NN_VpiHaP@ROUiwWAL$A|Pq@tblFsaO}beYp)Ym)@b> z+N@wu2qh}_HlC7UA_mNAz1mGmTsD{PEkW3DEGO?*?)6xXCAh^*8>8KO(C#oD@irpp z4t0QMFhw6UJ^TK*cpn=v%KwyAw4~2an)D=;^#nUMRN~dADr-w=2f!W08Bn2@1HR+rQaXJ zY{Pi|R6D6RF-~4c(OUi(OphF{|)ZgXtxAVdR`ssAS+FPsuSh?mH{2RN!2 z%~ET^no!%7p+m_t(RqkkN9{xBw2(5BFE9C~QXcN(iOS@X`l~AWaoLh#A@3)DyzO*x zJyQ>cD=PC76nX2Mo-UrmwB<=)jejRF^UU#6(}j~PxFE8k2KL0Q$5l}GlQpSTQLz>c zf+GrH&e^;is1g%%xR5~c?OZ=}h;<;XnS20BzdTy!%Yj5lFnGz9AFyJnJ(FBdEZ9zC zE_{r%cjm^x6;u6}ytE+d7$R1^i7yy&f{6CDp_(z}EoVfV3d?KLg;YJ3Z$%EE4RBp! zD66;&Uq(dCOL?%3SCjklCvS+CCqF)Q>8Zf$<=X@!@dUN$;!~WvMYlOvA{d3}^Y38v zqc;!3Xj5Tek9Gg>R({THK!yb!fY4Q%>3YdQ5Zh^<~3flBN_{hZ< ziq;8~(s~LvCUv&mS+X$k<+eMI&ZhtaF=Mjv2d)E1?}1$>a+O&o21_}5O&biS%W6oK zUPTTMB4;O_5c?M$jBv*AJ@=n$#bru0*J2O8Ju0BtBrYIf1M9$=oR+k&qH z<%l3G&VSd*tm2pKt!|gI@C~wKcM>rE+#_cXB(ZwPZCv(>RCeNNY~=g5E$Hp+!}Sv#A;dc z$9d(nKpM=DfBFRC$}}|+chRw0tTdk^2Pp7((Z6eX)gDT&LUTMltKH!jUxQ1<%sJkl zJtgqm?W4JztusMwkuAquKLulzE61!g3K%lxE@Hs=gxvY(5IKe*fDh567vCN95KAsmH5T( z3mNTb+yyMt1=ZbUuDMx`s7{(f^$uJ1mE7~FZ9Xlx3s$CM7U|2mO7Peg+?wfEu!zslkIr5St^qg@-pY-KK0(eU!vR~0h?rNStU#6-P|WDZ$4~= zKmlrx#g}`md?XhWI6WsMMN;3gLR40E%9eBb<=cIL+qwds8WZHLE6{x?q`^t?tX+6( zqj+ql+anc^7F7qw;kq;uD!w9|N2B4w(V+e-EhphU_5q>oon*UtS?C>K4G~MRt3n&9gqDK6Ty3I*nAaHtQ zM98N9wGr0VC~q=a?l5nkKhScA`QQxKlFL&6TT09M^(+Yo;)}w#UuBANHHg@s<#n+e zNpC}?O=qqTsiM6s4Z(}||g`Wx9OELag;c{z&I;oEl&z>H7?3(<%dI=pH7m=dheltdman>i+wC%dQoA@+XoWwUD697&VQ| z5}AYlU@pIukQN>g4qMFwwVguM1Dz*v3V#>(2V?+SE}alwmF5PL zxst}Xk7RqT$vfv2-riiZxG}zXnM)=b|9T8aSEa8ghtPmH*1OS+*vEAA2DdH?)2Iij z3`r8iM6t*Lw%&yhi~ag^0G}k{EiRqsJ<{WM5pwZNU%|HIp`Z${OlwpG{tmPGM*LrJ zv)o}ez9Cr5B}^06Ht6TFh(>gq%)vNCO$PFW4ffRwM#vGC{0QCQep|L7mOlL}QA*6F zm*6V<#f$kEOMiJHe_GGn8ge1Ap)--%MxYJ+6GliPW@S@J&0lN&=^#M^wfHI?mZ`avxdRD}@%k8&)I(1<=k6v6)Dg?5^Id!|>_`prd=cX7x_nU~a z0!MYxqBXJD6o*V-_XKRUCHIefiHW4~c>SM~Oa(7j6%e~z4i#%Xk+ljdHZIe06~|yh zukh1%!z24Pmgu2mBLAIYeT|el{6bSDZnVv_LjVOMCvfdS= z3T4mlhfZV&Odq?ZPBnu0@MAtuHQYI{W<#1y(T@j$TX&eBh_)m*RWZ6wJD8CxQw1I{ z57PcH6Rjv^`+CtFXpY-$MHJrscE3hI3+OX=_D+^Gkz@9_>>=N3+dqc3tL`C%{zwt zAC6Bb;FQL8!s;on1-1wf>@c>&DOeMNQfK zVr|ZJ(wpjgBzY&H`AFK+P#}^YBR#ZKfCb1wF?=8+V~Y9xr>4*P+kU6u^}P7t=D%WD z|DgF~+uZ7`k7)jtf86{inh%=8Uu*wir+>Qsh*sbK$F0`W3bbVY%E`fQ6DyF*jhKA5 z0@sUW5?~TT&1Q6nJgcy98-#1Iz_R_c5a}W0|HamEHQ(*i8>9VYhQBq-c7=K^*gl^YyLSjbGUpYAJQm-+G|$*6O-Mq)Db^K$U2-r^`og`!roLGu=i}n zJ)-VizIb>e-Th|boAw%3M7x*%=5S0`x*2*$tDEtbdMmH06L+6~xPdIK z*Upz*#_aUW<#lb-PvTcO-DVo+%dfAR^lX3Ez#?K|ySmBFHpI+hHZb2%DH!l(=InlQTGq_2%5J{5avcljHTbdmN)p-^7WHo+rajqJ7?@qv z`o4-d-b)Lc0JZJOCxCDs!E*z{?NR|Y|9Q*Y7bXTqRoaA(y?y<v0JDo`$-d!<2{hZq5wV`2-DaQAwlgtX z?Y=`?Z?6JnLu;}0*-ooz{asqC)8D$LXN?zyNZYhsJ*I(!Yzr;kh@*YbW+kJtPG(NB zZ&>RWcsat$A*C{~UEJonApgkhVH)vYZ{TV^E3|f`9d1u6fF3(CJKXfcZEcjmua^Mr zJ{TZJBNjN0C<0V%jYBSd_@TkR5=T;vAmpIlf6WH3yhBOm&R9CqiOf2jbkb{yXF*$O zru{*jnN3P3)wbM3xCy#VWh{H(HuEcKRj5BdCtl)Tp@;n&%wOk8b-dq-edfd)&;bkY zdxY{YLnpI7Bd*^c&_ga$4tl7nrPw5<2SaVtZq7?gvrcK<=2rGu(IeKmIMVbCJwX6| zpl+oH5ajN8M`wu&!E$@+#-H~&jFX}K!uzoRMoH`5AAdl@Nc>>op}?mJj^KlqeGjFt zhTH2l|Fl6Q%%!a%dzh7%X7gL`M|SNf@6B|++C*rQ6zIh3cts!lm*tFJ4Hz2YL>+&c znJ(U<+A_w@w$2O>!o*u(4&sSlzvKv>_;*bfpW)?R?owkb6EM{l#*Cje(9?rlakXTI zol(`_HR)Nu&yuN~kf{RmB#Qh@eW>5lToNZ8=24lwQ}vjat^;qN>f!2Nig+j$VUCfd zQD`mxD(v(h%&)WUgkkrUx9Qj9rr_WMD;}W~73>jN$&rf*#;F#RxoyJZ7nGlu{0L%b za4Zx1jsFedPjClAp6cswoeRy(!*8t!k#B0-6ZsYI6y*&~oJF7rxHM~X2`>9Z$$fg1 zXCDzP1t_0WKwV#Nw=O8#GPb$Q%qUgj$U8=^*8+Nf#WO|O6nBdrn4kY<;NmGtHxKXq zXFZ+{wTDpkcao%KW_n&noH9>XZ6wp3?Q4h{U-3ggNOevC+dm^_VlC)BZpx zCJH$ASp!J4!<0QLS2^k>Ma5w3Fk?Dt=)^?!(;*-waypJ?0!RejD$J)idf$!%k<~G4 zIq9))J<$inowu3TrpLnNf07lL(TDkz?ytt*l3F2W7A2;eW`p<4(+Y?1@_s+=mEf{_^*Eii&t2+SX zI>c}%xfRxwD$#@e}I(;o=2)%w%GR26|zKkFO_hB_YK2VfWie)46H3JvHd|6(H&% zx}2QyTbPI2>QMidwx`^s$$jKZE;*6x4>}2Drsr^L-I_ChuWjiud#%jxZb~4Z{9NrC zZFq*o&S^2srhC@0u}CXn~D_P)~UcEsSC_+rfQ3+ zVZcBPESdIR{jIr;90bGE4X|4JOX>u|PRcpq1MF>tNG}qv^P`ZW)@q?a+apc;@i?-& z;$r|S-L)}E`42_>0oS2!Gl~p^axn;O@;V-(Cz41-6xg_cE;`Lnt7Pde&&LpPU4)-) zpnjQop^kBCZa}-_>_s7ellQ$&*O;HgkeTCV%2SVSFQZ$j?wj3nGvfq#fAOH&&b-@v ziY*jJxU3J{Y5ZDUdrD$NDCtZHB^0boNFc^0v5lVN&Z!lzVu7OXhEK6&#PG5xmm>O6 zivW~fLOIlJ(QNOpF?PrctK*Im%O_f1)ch5Nr(d7*TfwKdc4}DW_iz!J^>&9@MJHpW zYG;KG)<#P3m}%G0B`CD>aqD$od5WJ5*9)!1mDUILd+mh4=GE5V#Nh94MK;|Q^@sIu zfUPV^%`M{UzoMw+J@fc};-U5GU*_WB%$I2Ik0x#93W?ckbTwfbRAJ~tTyAQuKFRXM<3#6cvKF%!@Z+bBrYANd>R*uBVK;JS}LcKW)D)F!HczHXlAbrbpslH-bUuwcTeaRt&VQingv z-*ELJj-T!tyn@CuJLbl`90ls6W|8u&PwC%*83}fsyG8DIvLdM};}rb@`|zxdAdULu z7_8F7+vqM9x-WEPj21!q!=DzMP?OeZUoUMeV6LqI(_x zNgu!sko8J&2A0H_vOVe~EJat59{a!rwqybOwJpo;;R8cV)t1_mxpD~trf2bOTIT?r z;7D9}o=AvG9H%4l+Yu5R_-@;Go69cJb3xI(MI{hD5&_fqyLLf@mu(Um9mMclwb7+1 zswGWPN4Z-8DU6u?)dMMvcs93#BiM!2XI+h55m3fVZNBz_Y((vLW92dqXoA=cXV8+83TlI{=t3Xi+DteGnSCi=~KHI%gDW|n~WB?!2V_zjV9lHi_ba0&Upze88 z!b{NTg$b9E>kfs3#(rN79bx{0o?p%L+);F9O<_%~9Gjq|$FoKFQgD`akveRdxL?HL z*<=sFz4zFg#Oqkb(@2_2n%f+kENL@2mimE9&t|->U}nm^S7r`1DAw3nqHrda%XHK3 zNZ0E_%*ID-$B0n6wS@3&60ZC^N{vZ{xyD7fJ%QuDsSF=DeuH9$7V!ia2 znSH48U~RgUz`DOMZ8Ay1WbkW@nheA67rH1@8LF961fAeT;XApox{7cax`@2{B{%|; z+EQ0AK}|(1W`zSt?vzd5kr;~CV*Dt2i=SkhaBq{|N_>AevB>8ZnH4e}ESkI{e!MuD zd52*rN_7Xdc`}#dUI);YlyHIkbHqPyn|obsaXl|PVrfYk57G_eWtk8KoWjfD6{mRX zZZ9lOT{AJf{F?K(47(r6qgk0&mJoyf4TKh5L>y36fGfd0^j7{Ek|Bkhj(v_i z*r=%*XX;9LIIbdzV*cG}$`+s=5Vxbw#ZJju5)9#}Hfek1HsC4_ir}3E%&r=eh-hpN z*8=C<#lt)^3rA!6xEjB!ta;tvXp*NBcXPGk>aGrlQ;CRX)JAKnH@Ind9bt4Ej!32N zJW2S7mkTXK4CMdsaG>&Ct|W`e8@-jcFdqGg_aZb`XzaCXnRXY1z~FRlGt2>suGsFq zj$$oBXJZT9f@byhg4nI9EIIcts0WLqgPwJl&?D`T6Mv~L#1Z^2*xSDyRV(4<(g8(S zo32%d7xOg=9YR^HTUjs0J8aU<$!oQpHau(2&#T*n7Pz-d0zG0^6L_3XZy@w{$H}>Tb=0uSd?c@%^s01;_m-~$ydBE@&J*uAb`9ePqx|Sn2+h9$VRUc z%*$Ir6r`REa#uu&ZN~wzs&Ytt8X9MPy~J;+DhgL$J%M~wjFnYE?K1D>$-PH$7XNM6 zqY<*a)M%XJEH6hDp31G6(`#$K3KzMtb$k?yD&(lxq2;rbS)rqo zr`f3DZfYMyvgu%Zw=p2FbP=Ue?kqwN-m}Ep%or|G0(C+I1i0ciiN1@kQAfuXPKc;i15f!;A_wl zyU&YX+hG>)_J&aa?mN>B7iw*$fG!v=Gc!hDiKW4{xd8+z#Bi z$!*r8gTd+qZfKF3-);k12^w^p7s8+PYZtxt@((B=PXn*x1U_XOc^#wpGO(-GX~vCu zn#I2TZyxHml!_nC(o4{)q(}X=LaaNkX#6Hu0A#K<=+q{%g5~@J{!yMM0#WWLiTP*N z$}8g?;xfoF=C$9zOhd`fMka=NYi8sBPdEps?w;~L@w>OCp4_MlymrC|HKI+JDkDR2A~iIeF%Wcce#)2jmKJ?{y9#-u)J7U@I|DBgHmsUL&BQr@ z0I&~X|7lZck1o{+Nt`#=ZH3ycZU+pS5j!u+TV=lXE9>l28LF;rNSsawru=nn6}9jN zC28D+04B(qk7r5T<)M08_&2Bb5Vei^SJEl<7Lhg~d1?#*Mmhu=I|PrzBaot0Bc1jd zKi?V4OR#1#A5oFauZyLo#i(H6UkSPJ%$_Iv`bniH?~|x*$Y}ce4_SciG!HDr>MhnX zoVuncO7BHL_Y^{k=s7jLnCR%#^b)^rZYnQzUsYHAS)+c8d;(QcT_a?d+HlrfF1ukAi$`6hsRl4Emmxs|M4a2W_xR< zl@O9SnZHK|5C;-fF7D#@W+SANwQ&KjEN|mw<&$W3WR>fnfjf zedBa#LugY+DdOMsd#?h+6s=U}bS#vfz!!VW;3Q z&$6`-vNN#fG@Sl=etOo?`A6-+kv@dpX;wChs0W>e)?jWVwtrIRje)wU{+=aM*)RP{ z^_BQY8^=Q!&*BrNro)x!c8Fv{C=@DMbb1#%s<*xl(VDQ5?%)aso80tsiW>hK4mNT- zuZTC3OMI0wZg9E>R>V2R{>!-vTr*|Kng3rvJ&N3rGGD{%aLTPnj@&OqEHmG{hnl)W zCcPJ_xwRCg2y7A+bCb8js?li|eA2(-KBrgkyMvQo=6N=lqen>-ZBJ1O@9-#obj+uU z$aZ^Vddv?vM}U)C@$(~(IOXGh?%!NGQK#6p%NNi5KdzrEiS={3UD6;?t$X?Da~8$k zAAHg$oVIpFXH$%(E-m_;U9l8k;zhlHTvC%dDOB04VwK~O(CCmq%k{^pgE8k|>xX9| z_O%4dqf&54LrfN4HfugNg#wYxsPF0|p09KXZG5k}27BR7SmRLjHP|aQ@CklGik^sclX|ChRd4~)9F z{{Qg}Nl3VEM8GI2QKOAQHHw$Spk^U~UD-fTR9Y43JIXs2w5jYO6bR5wG|Re5t8J~d zYOA%hwblA=l}ptGgapG~t$3;8rFFLJ1#b{V+3({yvzr98?|0vy_wSz{I@#B2UUNNj z=A1KU&YY1TZX#nohZcC7zezy?bTd5hIkITJn#T@&WI^ z-^JnFB3tjaLpBn(ny26YjCI?15ZYjbgsZfC=+ul_oHJjELyQTvlI#m&lWPIQOlC|- z^me!}yTg~gJvte}QCT8J@SZ+ZW3+NSn_p~@-lVe(+6&d}S9gSS{^+eXn&z#$A|trq z!H^o4HXiHgK7P+uPq*pFjsX0Sa_wcr4U8re&%w!@ay)jN=v82NYeM`MOJC$9L~~tjRt8h}8)qH;k`_w#WXVaEhOE-` zWDK$_XOS}8o+=binPa}jMd#I$1z%QCSOuuX9xx>R+R87l5{P}Mi2ViLW_OUauOo&> zOMdt(_jUnOZ?l{!1UMU3RXnG{!oA2-VBCSn4Bzr^sA`&|-_^c;Vu3_zWo@ z=5PdqogL2Vk;3J4yGKiR(adhGkndULh|z8qjzj@0!J^&!_vA|90@AIcIVyD-q?X23A^- z9E=lI>x@PITCOX>PCP8%mWyYQ%eDgdJlXB^fmQ5$Vc~>nZcDC#9V@$CMdh;?JJl4O zP-}kmg4#Nxz%JmoQmdV{w>|phWAm?Z4HI3SDeZxVi8-)(APoaprL(9GuR>QN@H4}h zIc#2DS+K=~ zev4I#wtP8U|LP*x(Vwi(cj38OIg4MxMtX<&>TYth9gt2RLcTAXm9c0J>$VbgyvTe( zaTSu7_B3V#Y|bXS#(SIEDGj6w3EDF$Q48ax#6?>Bz{1(BF-QJbut&iy!+3}SgRsr~ zg+qYB*uKOMb6j>iv#kY_X#=Z+byvuX+%#^1+*pM_N-0qU$mZ7h$X^^vU(Z?cjX8&5 zOye{T_5+o4e;|X#7_x*|E>Qjw6tm!E+}v8NK+&=18Sh;Q(Nnts4_27aRauki>O6#N zYj)6NJ7!-00_Rju-JOOTQWrEQ<6uA@UF~XoEX5J;uGWZcH!Cu=T6OE1>!~KmP&>4r z#ko+yOdz)2B+{e4?>X9pCYr<tH1`Nf6L&@-xHZWF&2=B@UI{>3(6qKhEh^AqFq3 z3+yomyLVS)E;>u+Kzor`*F3bnwTuWK9A-RY9RQF~*2Uf??Zis(P)of}VP>#SCHJ@h z&^p`C?J3EbmPu{VL>NO08>)^2(OGP3v(7y8Yz6?*K(AkO4W$VJaA)%ezKM1@aIaaW z3hglch!0AUZW@SIo2I-?OGzZ6hnHrS8&2^lK^n!U=7hPZ52K($+{9(FYJ<+oIqcO_ zZgqU}C*34;1rs>4a==4rdgUMwrA&{TwBnH>jdGb8(ri|>eD}EIc=0{OiwL#So~jmf zmCVM<{v1-Xn#$NSvtVVVu1#uAiPeOh{xR~AB`APEgD^O)1U5@P;>1;r#S}Wnske7S z;0jBNs`#WVtHra2Ji%S7W((~E1?_QMmZ#O-W)v}pGrmz<(cfi|CX7*yxK=pmd+Iz| zD{@_^vTH5$jed_~4CODWBqt{sHh*+@h%2w~hVGTSu?c=b0%7?; zKxM(R=4N7is|0o8p)TmhF53>4OPwVZ*~VsF%AI9it+eWFE5vRwi$Fy81&*hd7v508 z{pw@gn2c$)#ZsVlu1uaSZ_}E?u@z(4!_3$s)+4=P(E)S%5(HV6T2`zvkMEJv;$JSh zzY31_qGogs{@CfhSc2=2mtsvBl$X>Ou`Z(V)!d_yePF`H!T30~c%C}%0`xE^I!MZ&hTvB5@%^P2H0{TLw zi|77&gFQsq!VhZKEYvBjSgyjdw@UuDE45&3Y!(Nu$?T3yj^#{?gg!ZNK!gnC$1gc7 zUhPeuuBt*Oph}u)Z*~%~Dz$1DD|n;a)b@3KK4~jYCDUxpcJd=@?Uw= zl`r!wKE-SMSz4R^`egd+T08ou;rv>9xRy>aWX2eD1~#LJ9zgW9hr9Cx*Vt%o$BYj< zn<#8f1&X&Hg^v)KFsWs4(Z87WKVK3k=Vo-t9 zvqHf|x;|^0s*l)RqLEhym~M1#&H|Zn%QAJi!?c~RV@aUX!`~#03Oj2GhkkV3L%?NX zW@fEnhB6c#Zp&oKW~BB}-9>ux@36_;GQzD&61p=6UOdsEAlq&;d%K4KmAyl?`^VM9N1e8;9TuL z9u|GX7Q)#*imPlVaWH;NYaE+U5Mundj_8M{u*dEgKa+<5{)tY;MA3`Usa>9rYV$rW zhj@2PTH)n$_D-zl@3?pc7`E)Q@6#-A{IKI0U5(h@;}~_Vh`6kzk}5m=`(9KTL0kq} zJdI_|@UWsT6fW#f~NK&#Ee)T)DVU_O`ay;T3-p{hv`VD>`99 z03ST4ccB6KPH>Q%_Wqcc%YLq*@izY`%c0%uph(0XYjLpv-Dv#c^Jst-**cJ-ip))$ z$X`CAFgy$*^m@ut={|JW4@sy^9uDb#r=N~j=l+c!$-lLjuk2PI}s|#XZ zp(dN?tE%={jo7STb!nktM=Qq7yygdN*W=VMAOGOCRc;5fTZ`A=`?1)i2%yxPZX&n^ha-e%{ z5^&j#tvwUt09^;3ig7a#HQXI5U$#XQ z-zbO}gWr(&nDx2n7j4>NJ_L%QQgbYof~j6XRMwgp!euM~u6 zB%SeP13NWvW#XG?IRi+z-0;Xj(L)lr=)~DJO%-^5XaeFAg?!D>U7vSJ^K|vbqe?S< zzUVbz^&aQw6l0R_$m@lMZq3gDJ3w(8hsdiK7ZwUiisbn|!o}*~h41 z+TmOEEt~my^ZEH)9rapo&_$@K7wAH~;+xku4&R_;9$M+cCy}l>ZmHVQm0(+&l|q`~ z*s(TLSY&a^v{t}(xvkEoU@gtsOn)lXV)i>ZlSW$S@}d_inq^sJLx*5d(P-F5vU!@o z9>x_Uv@+st9kv?PXTrNl0dXQvvqJ)?`)c0(G47J703#k!b~U}lH#%{zQf2`BgQK}$ zEUwRVWZ=H-z7v3;ZvaUXd$^p>vO)cslc-N8%13ONlLCU&U7xt9cdUo#w%z&mLpdKL zVC0iBfwJ1Zpk?+XMy6kME;PXs$aO5SEL~lLfK#9Nv@3%fCh?ybreX_zMIO0}sd zaXWNc4gZ($6&lznR%CCDR@RQhgXvtH(xc2k?2ckTh$x`01mG5=$Afph@nSYD00)W^ zzbCJ{)nARjgjmh^{)tIEIRFbN=kcaQsI5GS()5c6MdAcrU@7VhgPWe%*KuQ9qMu5j zwFoXW$~K0UaKy98wk{CAqqu}t5?7*5<YUNX*K&}Rt)(8@)}b1TKd+7QLCPFe4! zMa18~k^{7?cek^8sLkGn@E1;4IjVgA{7`X)e}3agC|)8>`iK1*VPLSDM1P^H*X2NJ z;6?R`LYi!QqHfeD`q@|42hmbccBW+#Kn3>x=%iZFdK2jUOm^w2>`k44G#|9yIwlnQJ{o5k#5l`d~R zLpE0ChXj)r96Hw~@El~j*OK78XPQh>MSY^?lN65i?Bh5dMWl^d1e~^*kH&oF(n@^M zQ0rEw$-t$u}jhP?k_OA?uOM; z0sB(ZiuZ^U?I8_^TFsheLanO(1&Jrft|6jtb{?>P38`nb7F*1b$Quq|9IdA6skAUL zk4;n32h=KNsIu_QLf>qEnhctjB(Q=iUq|rO3WvMQANh=v<~82~2Z#mT92kG9Y_xJ^ z^2ch89W68eNGj_`WloGhHmkQJx}Un7WpRxT*WrQs1twB#oqu-gd^(X|ydf47bo};W6SWysFRerAb)<5y!uj95`I_00eX?{!9VVe{8 z+6& zytc)b*b+0dq8tCrILb}DZ)s7wT@^3j929KX9v)J|j|mUZ4@)Kumc>g8ukvzlbBl$& zI@LW+nc6i<2ls}Co9AB9pgD{buAdXjAH_}t*F&rKF<+s7_RjeiMyrlOi{~rQLjez^ zdH^oi$we37$7%u7by{(Yr3Y&cbfpsGFxHc!^3!5j|EpotoM{of9rd!8P6L`TJ1uq4 z2Wsj)M}y}HxsYkZ%7HZ&OnNnXLbQiX2p@_P?BH99Q7_zYNc<8TK(*G2LmEXhW~Zq- z8_44`(@g*<2_O@U_T+G%Y2tmy8;z}qHYMLuW+#LwZe{XWI}B=My{+UtD(NNyD^=2a zr#T4?{6sc6hVTA52~<%;6x^vXZb(#n5;>WSPKH`_P_DOenWyA-y1c|oTB0dTY~jV$ zu0IWlCwc9rv-Qb;c2m^)?%0OJPt%D2oh0oWkV68oTO7e={EkJIT8c<+duH`C(YW5x zvV5SWm8hEqpL+MsZf%&`8I>k_zkCg*e73FP7E9!X6aPk;aDOL-m^&%cPHTDkL%mz= ziTNAK`!^0}fw?G)RaAV~b&p$Biuofxo1`nUjkoKLZs3ENPN4bY&uGxE7)pz;>Bep` zm!6uQ%g%z>(PBZQ-W-l{H!+tSV|FID{Vzac6-h8Zp-69-P-u}k19wu1V{J2TadOT7 z43~qt)}i*V1V57xN%G1~?7#xcj)uc(2En|32I%~tl_wT^))YV)WG9q5Jk9Uh!};7h zk(fsUf9!BPfq;nVGpDQ4i3NcoGZlj#f1Pt-ObK|r^GCB63THTzTvQ{&LPTf?V!Q6a~kG@qS=wb48W@Z4*6g;Z6I{sa;Cu{nP0@M^dDgFiJ zbi|e@eHHOTKrkOZ0s6!*y~^6I!o4~)i;B$4L|aVoquQY<4Khf~&P(96#nFj5#Vx(- zn!{!nbv;0~9vX~NhcuZAyUb1YYv?OlqJ6yjuWrfI?r0RaJqhp*ymmCF2F)vt>>!r7sx;!hRoV|hB+x+p zgo`?~NzE8Eqopojy%kRk(g#Y2 zv}|%_ccWrWmH;IFY?-43ZYam`mU6T#r9BXjp&~CWba%)Xj&#iXnG;bgHE&VnO_b+& zAdI9zC!#^zDq5ed^)qqvXNe_uc$+T6y4jNR$vi~-IR%Mo9xMTK1z|6J<*n8gTa|k zkMPM7B3JFzT#KpK4ndH;_i1{kRVMx3w6;esP)EB$BY`_I9p3@`;cjv~xuz$TCbyM> z8uV1C#?G&i6ni!`v#&KfCoeH7-3Yg8H?NhrMhN0+#J2Q%e)|0>z0;_b>G$`0Ri{R+ z`pQO~+$)uuvcjH4gM6aDRhNwrhzVq;_vfp7A=#b;?f@NFTpD^>Pzlr)=4xKr;)F`S zqqxLRlxvj~Ny!&{tM1Y7Hj5b!Q7X91)8b8f3`V8jA$mH*%F%QON%jEE!>cr{dxS{{ z=KP}MKtY2|K2!1xTcHfwN{*TempGQt3zu$CW;?|+W_ognl-O@&5}_t*?D0DF1Z)QV zggZqkh216YFlk+*W0L;tHZ{`Dl444;JlKg3 zNd2g;x6?v1kp#W90bZ(efH`K3JQ)ERSEeg(y2|T@OyU)*JdsM?!S6WuDGi1El+ z5`wiX@pD66ea-;YyX`TM_)fC-^tAE|r1fT^zOpB;lA-s2;+Mn9*6A`6qwTsr1GSGa zCrzAbqYja*|H%c%sz9_Gq?trxY@>xV-#es^YJG9ye=p?)8QHkBD5>uyuFsRXHC^iY z=y*Cg+ja7ve+IbSI5+x3_KQTQzY^yTkhUALk64^@ORMHZ$SxZ4J(598;znL+LO0BZ zy855&&fg7l3+y!D1@p^ElE4h67tA;B2Tq)D?*@rI37Ub4xmJbtnnkAxa;AnF*xpJD z*1tM|PU50QIud(KNFXrp|A|-(AZ;Sh_;E14XQ*FXuQw1lahFxluwYZk9ctwzl&2SF z!uG`bPTGCLM%dC4uMlNl$LvgOw{ojSn6dy0gA%{HcqRPX^s!vzU{xKGl(cvG-~~AB zR|_o9LVw26RG(13F$!4d5K-d##8Xk?Bj}-&N~||Li9K)90%~NTrUo}mP0w$~lFS{) zJ66$gkOZHGqM5wG&S!7aZz)AHJT|qV{BXO7c!~oxNCG9lbCSTE+W~$ZI+sb%%ArdW z|H(qOSGAwA)y_g-xiqklinQi(EbntxM-l~z_Nq9wWckQ5pA zUzB|O(4=n(#49acShbMGq1MUU&|`_*c4LFs>E^hRSZ;)AKxPQ5W4E})Vf-(4;X*2Z zW(vzaIozqK+s%_z+$Xu`m2X`Ditfc_Iw`%`K-tXik z!d&Pg3^+j;@Lu|5q+VW6zsPY);yGUIddqFxf5C!Nxnw|?D6lJiJ5T%;`3wsX+dRh4 zs%LyQx+Ad=;Y5uiL8VKXs3$7DGXst^VNc)$Esf2%A26}MNzHue&zB^(P7eD8tK#JA z9F6o;FaL`J6h;*JH5gIY(Q0APAHCi@d6uU5o-rIm-4i;MtFG)VRa_9+ZXRxxy^svO z{0J4;CPgQdnm$voqiboz2iJrWm(;wxPRlK&FdnjKkyD@O;Pw=05383i(&T1=5ukco zGw|Q~=p0jJYsS6<*A?;#ca$z^r%1E^7?Hx+WmPQ;sb+kEj%_|~+LB!qC{K=N_&zq1 znWgoK|47%OvK>;Y?CXqSpNE3Me7Bda*6AkiEHe&UhK=L1`crU;ZS+<%=_$DJr^p_G z*kS}6gsL^QjF*W!Wo@o`zOu`;WF;u+rE>|#8a zUFD7_pZth=!uZ=Mcryw`Qs*QZc_6jKYUY?Un=t&ew6OQ#-dR?ozmqC_Ws5&6@enUU zCK+=iX1_)mLXqyNKQxaSI*n+H@LKM#L^E+Ab|Kx!Zq^j4V?uY`1XU#D`OyLt0LPHc z>M6+4@r^9cnj=Yaxad{v4FMIAnZnfWrWq6&_3D7p)Oc!{Tp zri(7&qi+%O=s^jSZ>#+)T~0FH{3=OqBZ*zftC$m)AQ>)J3>p_A($*oi+ot{vlF#I$ zt01c%X{Tm#C*QGi`VewN5q6gUeg|1kwB>FwGch!;i9RtM`mQ}(eiHrpR8wxFbbqCK z7Hw7nOP`X5*&p917iCFRwAp6d@i*5T#zr7*sfjnGr(b%AAAUx%&p*^7w}q74DTkf) z)v1$y|a@)r$K)XD7cSCwC|_#=PxD&0&g-ReQ6h}8{wp6P^Hga*Q5 z!fk}R3BMy8AXI)g&ohm1IpIGDKOp>$@F?L4!p-44&z}fS61?;BJZBN^CjN4M|3uhH z*h?6CW1i<^LIq(QVG-fSgl7q#5Ju0>^PEqZNmx#}oA4;1jqn0t58(?!{{?xTBM8S4 zN(oaadn~_~5S9^Qgm%JngjWgg5I!aJ{a&7@h%kdNi|{kTVGD^Pyg+!1Fkw-iXC7e- z;T6IIEAl)ik@sz$=Pb_i+(%fvG|%%Hzk?d{Jl`NJC%i@Y+LAoae7@aG_yu7#p@Z-u z;T^(>%kn%|5M~o@B&;CZOZbS;^!+@~V}vz?0g*h z?B+brkvHXe&Lw=C@D`!Z&Gd&bkuZa>fN+2iZlNy16NICq;jo~K_t&(lnJfUuJAIAJ|u^sRZGa>65o^@NuQLvN!DLKWc>!gmNu34bD- z3JfaR=RfW>1#)vG3Ly(2A_KNsppiPT7GVYcktNr&cC42*LPgs@s(B8 z6DCgd2QHix96r2e@|4=Z)Qc{jHvAIrrI$^w3``sT4Syg|`OV8IDo`1yyyD6kSIrDm ze(OK#uD+&hR>1$C_1AuT_MAD_T|ayFcN)HX!`x6f{cqll^A~(?;iAQjjZ2me_AVRj z{eGmW`KFs&q7=e^%VW312M7GO-ZpLciXZ&ohqvGHqaV{R`|l@r{`9V&{q*j;f4+8Y zpz@x3hu^pM7r(s!fnPoN>y_U4Z+`o`hXR#r*Z%$w4?pt9NB?W>+CTmIv9)U-U%P76 zDqs<)tX#cj^OyV%=(>P0@5Zn}{j~h##e*Y(oK*DWSPL9=X zWPwSo|8)#%lPNi0akapkI@}wC^cNm^t~slzp+hc|Bw;!^>}e zfa=11qc>Nw$q;1|D*A&$I=p%Xr;Bu^_S_i~e)d=^>@xvDRkUpR%`#tXaVHUj7osBF48<0c2f5#=f2AYd3$EY9C){%{-b( z8>rh5$i_E!M4dpeji@dQY;*}$O0r^Qk$IJ2tcrb6RX(>cJf*4(PpJXwma)9ubno$a z2%EaZW1elqWpLbI_=K-P$zb-9Yv`I6SQ?yT%j#kLrq# z!&g?7*A<2@^KtV<=#<{=!aY3~=h8R?INP*RGhP@L{oSEGs3eh=GA>!AIhth2T=Op5 zHdW;(5x@8&)36wzE_Y{is#@;VI8~MXXKhF=li_V_QlYP6bLgbVr`Lpz3zpaAhewz@ z@u-4Wbls1&I-8WwB2pTpS5K9CZG3)B#nU0&h^3De#D`5FSum=COpM+KqjmXs;bjq5 z5TM{;bTioKttzWKHayz=_BT?u)fL#+WBjq(AE7;hTQkiJM(@{aRrG$@3j1R*Ek4b| zi|9bv{T~s<>FU_+MMPS>ABI?7cE5JQ7;&=u09XAo9`c~4VEi(X~`V9SqNbe6nVNR?P0;v zU~PrV4E9i?ebGvt5#P@5m{d*O>;71F3?;BxIzRhNe~kUUzt{sj-aFc2@1wz}mB+OJY3Ca2uz2ryM<@|K00)FqaU)hwPL&0KedS~IBt zhhbPpcaXq*lc;g=qyNsrK9y<}<8o)c%!W(l<9&}7SAM@VF5}s{^w;edN$NoTVD7sJ z`qF8BPP(*CP3J1TO(RHW6^f)Y3rQ58z;={7XCH`t?2jLF?BBJ9bIuo%xw?Ua8a8_4 z1(>txT(WvfJ30GI=Yp}~w(u++J%0FdeeTMiHyx7v49X>Uo>iabGvb^9xj-kt@|T9s z33#960c@N7{VdPYvH6~Gv3#@braUdg`pNCDYSDtMmC=ov#Pu*c>Rw7LhXTd=e9Loi zaP?erP!qj6fe1V$F7Gq;yzu)~c)y7c8ubJ81g!5cclQVH;<<}o z&GM|4AzyowwQd&MQpb1eH=g_1n_2qO#hXkO^<|Iy@|^pUPkZyzUo>0EbNtr?ySwex z<;qaxqJKsPP^VCD#Fo0)uq&p7HyH=&OQriV$$ilr5TziipGSVC>pSUc_1l~Zb7mbJ zj9>djg4+jiyBLf=WV@ofZz>jh!b7X=GWL=#4Z{ZuGz=(Hoqx9t1%b$c{6#NB_UG&V zkU@{VAx+?OrvG9=%MC|-_NQ!LC+?xT=9isU4es~yHl0X>eGm-ngMco--5amx<6W8@n`TQ-n)l{g5MZG==w4#QT1PDE>8>Mb z(X6ExTpjAa^n$7o2YB`-`#PaQUVS@gt35fQ2G^V0!mh#fR{L+A`4T^;g_2Uc8bo>CSc$ir_O&Gf@=#e zbUAOUpc_9XK;g+Vg#~%kiQy9}hT3uQU}09Et{wk}5Xah@Xz@mAU@P@!W>1NZ6!;xk zT190#=f(}gT&&iU$lVPj{{`|`s}a}man{1yG+0dlUh~aWw9G;75oW&t}7QTt$Ib3XUp$ zU+ti~4QiCj3$u{24&QV*MpR$bYZ}pbeK3Y&TJ&!nCIaFyTX-g`^TLkH;}`J4K7pS= z-MYnGsoIXNe0}w(b^aJ@3ziv?bvRx!S>gA6DO@K|sLpXyLGnYLR8{5n0tQe40+5@Mo8@Mpk5-7_YM_-)bT%&3rWiN(P3Mj zZo)ivd9|mhHFU&Nj0#`3j2#{BBd@Y;%d?tV!yi<|i=Xnfy_M|)GrlW7$`F^8N(sHr zjo0TT7x?R5b4(?k^D}$x%7makzZ<&JX9a~I*C|!J)Hp>=RL(%8t;DyS`=bJ}?PE*5 zw@ji{W6PHu?tQWqS{xr3jR*bPA=IUd6L_)oHtA?sAhpgH?Wdfz7TqReBkH4>}}c3$1Fb`G$?*|mWR&6 z3|DYHfmToS@e$WYZ!=e2dFZDO>RC>gnXiBPu29u=$^6>B2!nr~eyTUOrxREzbyGUJ z!7S2K9qb$%qd-Nfkvjt=ki-VQYQCjU_*uApl ztD=pi{^(=`J;z(yZ!${gDbQE>L>s~fQ4fR>d^b6wyUk}Dd|E#LzF=1t(?eE#`6K3iJfj@fd zZennzZ$6+8(I( zY+m;WS-%QnabT9x9m?Cx8GNte$_#|o$z>u-u@`K<10WOw%4%;yCFy({b8@oGi`NUa zRYU~8HOXA}jLX-Gzb*PrfGecbpeANSO~w0QaU(Tg1Sg=ndWRyezYr>7F3x?9x%d*N zWGq(tqkV0P^uz_#RU#wfP~93qX_Db*_5JWgl))(jU#uPBx&~Cgqm^5#@tHPg-b-27 zknDrR-NBRJTeXA8YhSd&nE0!W`K&F>G31q7l@0k7GfEe2pb^tUCvZHk?EzX8&wZ4iFV&ae zP8Lz~^RC0Na1p5rn!X~{dQ!Por5ev*Xo8G3X{Yrnu_iW3(SLoU7L&X17Kno*OIuiVp7_km>Mijohb{ z`=G7Y&ALqeTv=p(OFF!#0q&P9xYfgXUy;@pd!*h7|CQ7kkY1kEj$2`Pi?m9 zi`WD^ICQlWDxCp`pS3k;SfXJmGKu%>Rh8!N-`1S(&T!G88I<-3(gtHQGKJpCr0!K{ z(Bk$_NmEs3?}B1o*i+zP2Pbxw&vA)G95~RQMoP2*=HB;_&K&e%ErPmS?=A(L;ek3w znYZyKWx_2HVie&XGbNgUS%z16d(n{AE4z)NAoL+H^0 zQP4ZTlcAsvbG{y5oL6yu>EaVP`dmh$Ac-vY7uw``To94U3cd3+if!K?_*dF}h`XXI9p}zdh z#fUDp0Jv>4eI{w_P2@msxQ5SM!WSR3voXDte8IFcTVR_*{jr6GO8zXIoYP1}z)maW zD>P_t;_=rXV-*PJvGP))(<18S%Spe=;ccl(RASVjL?e{a2)_jz94A;}Ogx zb??Xjvs3AFvFC)mg!JY$bx&KF+q_f>g^AB7vN$}0Tg@Vp=ePv9h*2-ZXPU%_Y)*Mb z{UyxC!eHHoAUA^Ru}6vMMn6zwZtnFLjBP0k>YmEMBjk(!VeJ zXC1RCUY)8)TI&G2ZQ-2)0>7WSEZ;Wq;ds5;T|VB9V0?U@U$?&Q!oR?eg0T%6!L6q7 z0=3u9f=0$g4Tw%AlqMFT1h76R1JTRb?VpWDg}DdaO0k->(o%E!6CxhO~+atW; zy>Vk8QnyNo&_P^?xSG1%yloE%-#-T$iM32k&*EHM(peu=!yN2Q=O(@qP*^=k%n! zt|#RXQYyvQZ6RCC`ENtq>4!FmwwUb?*=C-YZsu98naSL>PIQ2#{RcGeO~v=A zPF0xO$sn`z6WxXYyoU&p6-b$^=n5=|3fzbBLAMv}+;`f~ zK$iY)H7|94zR7*Q_MqHr)1Sk$nUAO9xQD^K3?5H8S8s-t>$Gm#o8KKFjb(TmDb8{! zzP+_4#T;OBgG+G+DaO08xq~6il%W*0q(~0YIP7KDQ@`iW%+TYRy__(?dFI1;YJD?$ z$rmWgN`W4u2KVTeGqCMPKI$b`pr9zF>uG_ccS3fv(h1XY#KDfOE42WQC46$M_6A}E zi@;!XKIcB?uL?%bH3Q3a)g|i1pGqd=QuA16#HlEr6jM@0#Rc z%iet3vp2Q61QLnHT*?>o*f_yT@^9!2&ZLDxWO$IGw7zXm4zOZUYFIw?`J)pHB_>}a z(7riC#)K(fTK2=XP7xI;ZQyEGZJV|;KcpNbKHegNy$t(7sunBTW(Hj*FIlO?eWKSr zdH$e0@taqHWB&M*dT{0AYlN|-G`4-BUy7dgiP{oYdy3j8UeR|!#UA4+2Q1O2bjoAn z#K~D3CtgISa{JTjWm87idRFjp$#gQ5wcjkQ6ufdtF#6OAiXqo6?x%$xBJbn}N#Vdh zpoj5cH!_ta`9A`?-zLEy)OXFH-3SuW*%K!;rRbm>~8d2Cz2eW`+l4-Wr%kwnGp!fI8F>#9n9V2(|-c#3zb z`1O6Ylblo8awEbHR_Ab*zO(`{(C+lU6x-gj*SqCv%aP3QuaHRikS#p15gP}%g=!7P zrW9r3xt74Y#-R9a%hz0#@pJ5XDX_VBq{G!dI=!QNUVjW(918lpRU4uct3W(&6UzFO znS3v65O$BL?=s4U)LfKr4m%)ZpMp~rte34Up29ndVt6mZvt1FrD0+roL}A(Xt^eFC z&&J9E#*kM|f#|ogrJ)qe`^keR-{(f*6ONmu+XSMi73^JazQ@%Q#`BZ}Z(0uZqG&N<+)GnNH4{*r-8@vWAvR zhdb!7&anI44jeROJPoOe4SMJFKJc>K*ZFZXaNCm>O&&Uvo7q@vSTW?rNrC9tV((*c&%2A4sw0tuZ!_P#S@y$mUtjBPC$S$=nD+6XFC;Fw3{GC^ zj~ovKKE(9F9DAmG6o%)qZhM<+4v_LBbQe13n7%*aH_)!LwQusB;h&`*W$tyFIIa$q z>@@1KB728wG`CWjy-u7f!O+G{Wq`&N?ybBbD?DIh<#+oebDwe|N~gISFmNFN=ww$S z+K7%kU3%5}#BKOAr@e{x8v7fFbtE2Qn56f}SJNr-(=jib&DI=cySzq0VRUF7azI3(|#|BJkdxg_p^U-uObYKbm7+=Dn?10arI8I5@|ut=@&r`tjF zi2~Np)A6N>p!jlVXpnOnh_@DGTYRY>)RI(lN>i3a3RoXPPDwZXpg#Lz9d!FgQ#5D) zjczAy|Cz@_y9K|lso)f9KOV0!^v1%JZuS0MnSS5=78M9y5_*uvLdAs^>i=$E?|c<5 z|Nqd}bo=^I{pQ54`|TIZ>2l%$9@Wk{zdQs7hy)%RenIrV1CI~A1=ET-nqi?Zo^!hn zHdROuicP$OXDx~hX5Q~3Njmkg(@D*@?zFpzux-LM*db%2;%U|leff8q^;Nc$7O(KT z(}iqZ!-_VIT7lS(gHY>h5O)!uKTwy0_~F6WV2fNjqwqfqlmOI=}&kv;U9zkNrR9@B4quKl!MCxBnKN?GmCo5-abQ9~+Bl3$PgkkL~lX zdOdIH^<0fbs-2T(;!@V;Pyar`WYGJ^jF0BR-M6~;oHIVR@kd|a(mwzODBUj7fhBr8 zrt^z~ad!Tj!pL4awfVR>nOWq!#1gW1xqAlFC=6DGa3dTo>xFRg4IIwFGeZMf7)~Be zN2kD1cQ$7@N)OTN%wVRL!=V>6i)!s^KnraX^h)_m8SjQb(E&krmF1ANGmWBJ=nf;^ z7H1`l%$Iih$@J9ym3ZyNU3v_V(?60Zs(K{_BxsQum2TL3?_Q#ic0M7zN1w%tE;N%w4DzKd{rE(P#~$XQ})o z@lQBCW1~0U$B9?O_tOmLg$(TNAmzua-?2BV$?5r0CBy2f!E)&i^F9jfs@QIZXu8x@ z4d$l1A)maK1~r=7?U*<=O5}*3tR?P93!fcspna(yHdyvXcB$Jc&0UkYih~VIm_~za z%J|VWNsy;(e;kK3>BEaUCFB?+wJ33EYQ=pRUy%%iuzC)#Nc<2bmPUQ1TO~@mx#B8T zhX=vT>4Sk9Tpp+eVxI=m=$iqU?uhRJSgRmm0oIMx?iQACe<`8fN!)7_V1**o+_%e5 zWUYS$hhwdq&=Wee6<8$YOQb`{JfKF6M8VUEC5mI?e*B8ndP2~^Ma45cp=t}#Y3c^( zyka^Xi*#klkkif^#B{oAB^E$kXhBV4Y=XZ8=US6J6|}Qcp;AEcXY{qDzAFS5?z-uv zXT-MkQr~6%W$!GdOFB>eL5-x87o}t*tU`{TNw}I&PiP>72=jv#t>ONw@bQ&Oupi>HAK9y(PBr|>Ni*$X>5sRYehTdJaKRHjxTiE_an3>shv1IyhGbXoKRzcqeyAk zxTbMbEr2gGl&%M=HS4ROn$5`|2@)a}A1mo)^k*tRJ{M!`hk$^&h_y2{P3JM1XY{}7R^ z?wQlP%vWBayKgnikaT@jn%=1ucQRv>L$tyB@}2urk*+*zZ5_&sIfw=0ZcT!6v51Qf zOStHegR=IbLnQIuG*6p3UqSR73(CylbyVLohyTQzj8vEMD-^V7>EXnqhlWp)tb)GKWwfl)AJh;YP{|I&bK9` zDcjf6bW!aP2Bj%n8c#*pEJ{HsMc>@Ko!LXoxE)TqE-I9vcC-N@yB9C+z8LS*9@-yM zMV6{yzN20z7m1}X@H1VfVHD7Y$ z6W&RlRfW$2t~a)R{M|fwu#P!o0Nf%?gl9zC=1%+7d z*viPKR_tN8WOz*j!4u$fra!_oK&6wzCVyu-`-3NF$>Er)7Ms;SG$Df}we@1#oCu-@cW7Ty01-?l3grkG%18RUH` z^{Y6y`rVszbG|5Z7BJ1)-t^5#%cao#jJ~8d&Es-4WUt*$LzJd@YgC`3R?!OD7QTV=NhT5A_#&ScD|1q zl1a^?u5J&j?8pJo?$gC-L0h-?^awa_3KI?$aI}s=+?cPXXG(sk7j0|+ zi7I>9op9%{=I2qqrDYsUgcJDKAWj*N`jD&VEJ}zxT_RaS_Y*_cj>MGsfNb7FIR`pg zvX(B$$_nS2t>a-BHeokU()RNI%y%{W4&u8_M5Oud-|G+NyWc>?pfS;O`gwAOSkk(< zV@YMnDe&E)d7N6TW9vS{de4Qc)@Zs(NtYQbah&k~h3#&xyclRem|hITS|vizpvq5@ zC+R{A>_k3J$~&1Q19I7uWsccOyFrcLjO*fJTg_wHccEX%!LK*$X_sn&{iP`zPsfj(w;XAEu6 z^x@!|kqnR)sJxJgQ4>45SktGWn@QP?J7i-wdF~{QFSa3>YlT^n)AV$ZsF<<1`+tDD zj$8DmA6BFu{*`c-aY8S+zwSvctED&K>rAGHFYTDSM|AC$NW*!i-?y?gFzCKcZIXMEN5AI%zvF9}C!#7y*$B|uU1D#RUXurX&>(Vw8Ma#3s zyo~G~pPQ8&?tQYueEp>K+#l06W|u$v!16p!UH&7s-n(Yc$YxTaBk@*&UQz-uv{ymi zd+onr$$gMbdsXa1U#xA+PTxKhCFp`UzBH5-9ER?DFO28x@JGs^T5p8T;Rfh2RTcI5 zVI-4F^8K-V^DgaRbcd4{pkz@%YO6m!@FIUar!(2U>UqW>dE=z`*sJ}KvHOKG&nf1b z$fEuEsqlq1kFC~Uw=RXO{s!uX zHv3O~`wk>qS#IJ4bIyJ(h4&+A=j<2d>&9;2P@CDf=rJ_!p#{#?AkE{7s$$=wb1(XK zd|s9Pt^Dz{zU^)NH<*>fL}o`h?&Mg{#lbq9s0z<-hQf^5TRu0xu_O>jbc-z6;|UF^ zicRU_YDbBkhG1bMqIyg5wns!5sQQ=c1_KU; zZg(p)xQmyhx}CuoZf2(!R>#H{R99?YJOY17+?G95yHSOa1x1)H1}ml(EXl@eoG)fa zEPs8Zt50~bFS4-26B=sMyb-_=O+eRWl;`Y7?)1ej9u1M~8(A>g6M{9o#HC}|zQ}@N zkB)zhFZ9O-W(Udxg-eDJNfk%+iY!<>kTcghxkhsqJwq{v`(nP)&8>|C)V{u{4qwza z+80}wXke(Ahi+{HHfXVTMr1*$kXXv&B%6%BBGG0Pm4U+4c7LR<2zb>MdLnfN1h`+_ zXlcBG%;j$EWTQ}uX_BxDa=D08f99hK(SC^I)v!u`tY2oWv|U!)KYU2XthQnNzUVBr z;jEBWDLU%pGp#|nQY7U+(_F!KTNjJdufmh`nh7rbI<{9q_?$=nUpnqaHX+Aht5a1Lv#{#g?PhRmv5shKG zlnv=7-pF}xOUxa1CpkntSFVzwU1w|$1JKXRI1}h>OdgNY3@yUvgFHY|A|YPDQxnhh zM+-3@$hEXbXj`kvHC@DUrkz~9mOWpBomRFe(bDGaAI2x0Y<_W=nq{pG@b)?WtM1`r!Qc>%SNtGKmIGm-!cFfp7sVz)hSZaPXrf1lkPJw9?<0w;Yp})>o!dw|v zF|I%>!Hbvy9e6fcXTDh4I}fSw5HQBAm2J6WT4NU#lus@3{+vjMzp<$x%8R%x`4;wk z_zcZp<>t+M+j2r@>JH)~_O|s25Ag$5`)>(bvWv3=sn*E%m{)y+<>Lw$&k07y6}FEn zMxgbq-`kcII#wAkP{woptBBOUrDSXz6-ad?PvS$Uj3nXQ$c6c-^}R|O(5s}#_beGG zA6L*gpnY7C3g>G$-~a?AZ$o>|_KJ@W>UF7!&}-u9f(+~jjuYUR%R zr9PduaozwM+e(7uoAz-4IX0S$j$`rh=Ue*D(E#}XSc$$i}N|P1)E7NQl+~5!@>Mh~Eik|u1OSdaqxD0`P!O>#Yw5zENhL2-Y&Efc!WWb`mW0mb zKuqM*yv8$v`0O9EGqq{{NgNU_jF0WVgFTe?n)0FRIc0QNR&v0SzLCb!=;Pv3QWZNI zUyXgHS1wmmQ75X@qR2v#dHFcJ70)n@XX$}-0%s3x{EmWHmS(RMbhm#JGAJ_L~X3vk&{(D06N32 zf^e(=x$sMJhvQDLNKCxWDc$eHQ)cb zRhNo=T%HK?lHpO^_SCTtRu_?z*iURi)_%FoaN{FpcTlSJO! zm^tGRzE>%YB-jmLt;83O2z(DR5sc^Feu9o*44dm9 zWVfB}wxrI;fIW7+8QO*9@l+)_P#v*uihXDlUZg$M| z6q?4zV04r}Iyc|E#wyPj(^1PBTm^NAd>xIhN$zUGNBUCLg+92jucCVBB5xpGlWz`3 z8_CCa!(X?zQ9QR62D*uD!ymOpC$UM|X=bo+Rc`KiAiWN-t{{t=lS6Zq;`31~26F<@ zql*t(Hrs^-Ba2zH&J3MizOZajZu!D9 zmmpMjn(ch$+X%$M_VH&5DUb`zv9!9Pz0&)m_UOVgq#JG>tYf7!yAUGA>vSMf6dgYr zgkvyDd&Cc|dz{/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

tableList = new ArrayList<>(); + tableList.add(new Table().withName("table1") + .withParameters(ImmutableMap.of("classification", "dynamodb")) + .withStorageDescriptor(new StorageDescriptor() + .withLocation("some.location"))); + tableList.add(new Table().withName("table2") + .withParameters(ImmutableMap.of()) + .withStorageDescriptor(new StorageDescriptor() + .withLocation("some.location") + .withParameters(ImmutableMap.of("classification", "dynamodb")))); + tableList.add(new Table().withName("table3") + .withParameters(ImmutableMap.of()) + .withStorageDescriptor(new StorageDescriptor() + .withLocation("arn:aws:dynamodb:us-east-1:012345678910:table/table3"))); + tableList.add(new Table().withName("notADynamoTable").withParameters(ImmutableMap.of()).withStorageDescriptor( + new StorageDescriptor().withParameters(ImmutableMap.of()).withLocation("some_location"))); + mockResult.setTableList(tableList); + when(glueClient.getTables(any())).thenReturn(mockResult); + + ListTablesRequest req = new ListTablesRequest(TEST_IDENTITY, TEST_QUERY_ID, TEST_CATALOG_NAME, DEFAULT_SCHEMA); + ListTablesResponse res = handler.doListTables(allocator, req); + + logger.info("doListTables - {}", res.getTables()); + + List expectedTables = tableNames.stream().map(table -> new TableName(DEFAULT_SCHEMA, table)).collect(Collectors.toList()); + expectedTables.add(TEST_TABLE_NAME); + expectedTables.add(new TableName(DEFAULT_SCHEMA, "Test_table2")); + + assertThat(new HashSet<>(res.getTables()), equalTo(new HashSet<>(expectedTables))); + + logger.info("doListTablesGlueAndDynamo: exit"); + } + + @Test + public void doGetTable() + throws Exception + { + logger.info("doGetTable: enter"); + + when(glueClient.getTable(any())).thenThrow(new AmazonServiceException("")); + + GetTableRequest req = new GetTableRequest(TEST_IDENTITY, TEST_QUERY_ID, TEST_CATALOG_NAME, TEST_TABLE_NAME); + GetTableResponse res = handler.doGetTable(allocator, req); + + logger.info("doGetTable - {}", res.getSchema()); + + assertThat(res.getTableName().getSchemaName(), equalTo(DEFAULT_SCHEMA)); + assertThat(res.getTableName().getTableName(), equalTo(TEST_TABLE)); + assertThat(res.getSchema().getFields().size(), equalTo(10)); + + logger.info("doGetTable: exit"); + } + + @Test + public void doGetEmptyTable() + throws Exception + { + logger.info("doGetEmptyTable: enter"); + + when(glueClient.getTable(any())).thenThrow(new AmazonServiceException("")); + + GetTableRequest req = new GetTableRequest(TEST_IDENTITY, TEST_QUERY_ID, TEST_CATALOG_NAME, TEST_TABLE_2_NAME); + GetTableResponse res = handler.doGetTable(allocator, req); + + logger.info("doGetEmptyTable - {}", res.getSchema()); + + assertThat(res.getTableName(), equalTo(TEST_TABLE_2_NAME)); + assertThat(res.getSchema().getFields().size(), equalTo(2)); + + logger.info("doGetEmptyTable: exit"); + } + + @Test + public void testCaseInsensitiveResolve() + throws Exception + { + logger.info("doGetTable: enter"); + + when(glueClient.getTable(any())).thenThrow(new AmazonServiceException("")); + + GetTableRequest req = new GetTableRequest(TEST_IDENTITY, TEST_QUERY_ID, TEST_CATALOG_NAME, TEST_TABLE_2_NAME); + GetTableResponse res = handler.doGetTable(allocator, req); + + logger.info("doGetTable - {}", res.getSchema()); + + assertThat(res.getTableName(), equalTo(TEST_TABLE_2_NAME)); + + logger.info("doGetTable: exit"); + } + + @Test + public void doGetTableLayoutScan() + throws Exception + { + logger.info("doGetTableLayoutScan: enter"); + + Map constraintsMap = new HashMap<>(); + constraintsMap.put("col_3", + EquatableValueSet.newBuilder(allocator, new ArrowType.Bool(), true, true) + .add(true).build()); + + GetTableLayoutRequest req = new GetTableLayoutRequest(TEST_IDENTITY, + TEST_QUERY_ID, + TEST_CATALOG_NAME, + new TableName(TEST_CATALOG_NAME, TEST_TABLE), + new Constraints(constraintsMap), + SchemaBuilder.newBuilder().build(), + Collections.EMPTY_SET); + + GetTableLayoutResponse res = handler.doGetTableLayout(allocator, req); + + logger.info("doGetTableLayout schema - {}", res.getPartitions().getSchema()); + logger.info("doGetTableLayout partitions - {}", res.getPartitions()); + + assertThat(res.getPartitions().getSchema().getCustomMetadata().get(PARTITION_TYPE_METADATA), equalTo(SCAN_PARTITION_TYPE)); + // no hash key constraints, so look for segment count column + assertThat(res.getPartitions().getSchema().findField(SEGMENT_COUNT_METADATA) != null, is(true)); + assertThat(res.getPartitions().getRowCount(), equalTo(1)); + + assertThat(res.getPartitions().getSchema().getCustomMetadata().get(NON_KEY_FILTER_METADATA), equalTo("(#col_3 = :v0 OR attribute_not_exists(#col_3) OR #col_3 = :v1)")); + + ImmutableMap expressionNames = ImmutableMap.of("#col_3", "col_3"); + assertThat(res.getPartitions().getSchema().getCustomMetadata().get(EXPRESSION_NAMES_METADATA), equalTo(Jackson.toJsonString(expressionNames))); + + ImmutableMap expressionValues = ImmutableMap.of(":v0", ItemUtils.toAttributeValue(true), ":v1", ItemUtils.toAttributeValue(null)); + assertThat(res.getPartitions().getSchema().getCustomMetadata().get(EXPRESSION_VALUES_METADATA), equalTo(Jackson.toJsonString(expressionValues))); + + logger.info("doGetTableLayoutScan: exit"); + } + + @Test + public void doGetTableLayoutQueryIndex() + throws Exception + { + logger.info("doGetTableLayoutQueryIndex: enter"); + Map constraintsMap = new HashMap<>(); + SortedRangeSet.Builder dateValueSet = SortedRangeSet.newBuilder(Types.MinorType.DATEDAY.getType(), false); + SortedRangeSet.Builder timeValueSet = SortedRangeSet.newBuilder(Types.MinorType.DATEMILLI.getType(), false); + LocalDateTime dateTime = new LocalDateTime().withYear(2019).withMonthOfYear(9).withDayOfMonth(23).withHourOfDay(11).withMinuteOfHour(18).withSecondOfMinute(37); + MutableDateTime epoch = new MutableDateTime(); + epoch.setDate(0); //Set to Epoch time + dateValueSet.add(Range.equal(allocator, Types.MinorType.DATEDAY.getType(), Days.daysBetween(epoch, dateTime.toDateTime()).getDays())); + LocalDateTime dateTime2 = dateTime.plusHours(26); + dateValueSet.add(Range.equal(allocator, Types.MinorType.DATEDAY.getType(), Days.daysBetween(epoch, dateTime2.toDateTime()).getDays())); + long startTime = dateTime.toDateTime().getMillis(); + long endTime = dateTime2.toDateTime().getMillis(); + timeValueSet.add(Range.range(allocator, Types.MinorType.DATEMILLI.getType(), startTime, true, + endTime, true)); + constraintsMap.put("col_4", dateValueSet.build()); + constraintsMap.put("col_5", timeValueSet.build()); + + GetTableLayoutResponse res = handler.doGetTableLayout(allocator, new GetTableLayoutRequest(TEST_IDENTITY, + TEST_QUERY_ID, + TEST_CATALOG_NAME, + TEST_TABLE_NAME, + new Constraints(constraintsMap), + SchemaBuilder.newBuilder().build(), + Collections.EMPTY_SET)); + + logger.info("doGetTableLayout schema - {}", res.getPartitions().getSchema()); + logger.info("doGetTableLayout partitions - {}", res.getPartitions()); + + assertThat(res.getPartitions().getSchema().getCustomMetadata().get(PARTITION_TYPE_METADATA), equalTo(QUERY_PARTITION_TYPE)); + assertThat(res.getPartitions().getSchema().getCustomMetadata().containsKey(INDEX_METADATA), is(true)); + assertThat(res.getPartitions().getSchema().getCustomMetadata().get(INDEX_METADATA), equalTo("test_index")); + assertThat(res.getPartitions().getSchema().getCustomMetadata().get(HASH_KEY_NAME_METADATA), equalTo("col_4")); + assertThat(res.getPartitions().getRowCount(), equalTo(2)); + assertThat(res.getPartitions().getSchema().getCustomMetadata().get(RANGE_KEY_NAME_METADATA), equalTo("col_5")); + assertThat(res.getPartitions().getSchema().getCustomMetadata().get(RANGE_KEY_FILTER_METADATA), equalTo("(#col_5 >= :v0 AND #col_5 <= :v1)")); + + ImmutableMap expressionNames = ImmutableMap.of("#col_4", "col_4", "#col_5", "col_5"); + assertThat(res.getPartitions().getSchema().getCustomMetadata().get(EXPRESSION_NAMES_METADATA), equalTo(Jackson.toJsonString(expressionNames))); + + ImmutableMap expressionValues = ImmutableMap.of(":v0", ItemUtils.toAttributeValue(startTime), ":v1", ItemUtils.toAttributeValue(endTime)); + assertThat(res.getPartitions().getSchema().getCustomMetadata().get(EXPRESSION_VALUES_METADATA), equalTo(Jackson.toJsonString(expressionValues))); + + logger.info("doGetTableLayoutQueryIndex: exit"); + } + + @Test + public void doGetSplitsScan() + throws Exception + { + logger.info("doGetSplitsScan: enter"); + + GetTableLayoutResponse layoutResponse = handler.doGetTableLayout(allocator, new GetTableLayoutRequest(TEST_IDENTITY, + TEST_QUERY_ID, + TEST_CATALOG_NAME, + TEST_TABLE_NAME, + new Constraints(ImmutableMap.of()), + SchemaBuilder.newBuilder().build(), + Collections.EMPTY_SET)); + + GetSplitsRequest req = new GetSplitsRequest(TEST_IDENTITY, + TEST_QUERY_ID, + TEST_CATALOG_NAME, + TEST_TABLE_NAME, + layoutResponse.getPartitions(), + ImmutableList.of(), + new Constraints(new HashMap<>()), + null); + logger.info("doGetSplits: req[{}]", req); + + MetadataResponse rawResponse = handler.doGetSplits(allocator, req); + assertThat(rawResponse.getRequestType(), equalTo(MetadataRequestType.GET_SPLITS)); + + GetSplitsResponse response = (GetSplitsResponse) rawResponse; + String continuationToken = response.getContinuationToken(); + + logger.info("doGetSplits: continuationToken[{}] - numSplits[{}]", continuationToken, response.getSplits().size()); + + assertThat(continuationToken == null, is(true)); + + Split split = Iterables.getOnlyElement(response.getSplits()); + assertThat(split.getProperty(SEGMENT_ID_PROPERTY), equalTo("0")); + + logger.info("doGetSplitsScan: exit"); + } + + @Test + public void doGetSplitsQuery() + throws Exception + { + logger.info("doGetSplitsQuery: enter"); + + Map constraintsMap = new HashMap<>(); + EquatableValueSet.Builder valueSet = EquatableValueSet.newBuilder(allocator, Types.MinorType.VARCHAR.getType(), true, false); + for (int i = 0; i < 2000; i++) { + valueSet.add("test_str_" + i); + } + constraintsMap.put("col_0", valueSet.build()); + GetTableLayoutResponse layoutResponse = handler.doGetTableLayout(allocator, new GetTableLayoutRequest(TEST_IDENTITY, + TEST_QUERY_ID, + TEST_CATALOG_NAME, + TEST_TABLE_NAME, + new Constraints(constraintsMap), + SchemaBuilder.newBuilder().build(), + Collections.EMPTY_SET)); + + GetSplitsRequest req = new GetSplitsRequest(TEST_IDENTITY, + TEST_QUERY_ID, + TEST_CATALOG_NAME, + TEST_TABLE_NAME, + layoutResponse.getPartitions(), + ImmutableList.of("col_0"), + new Constraints(new HashMap<>()), + null); + logger.info("doGetSplits: req[{}]", req); + + GetSplitsResponse response = handler.doGetSplits(allocator, req); + assertThat(response.getRequestType(), equalTo(MetadataRequestType.GET_SPLITS)); + + String continuationToken = response.getContinuationToken(); + + logger.info("doGetSplits: continuationToken[{}] - numSplits[{}]", continuationToken, response.getSplits().size()); + + assertThat(continuationToken, equalTo(String.valueOf(MAX_SPLITS_PER_REQUEST - 1))); + assertThat(response.getSplits().size(), equalTo(MAX_SPLITS_PER_REQUEST)); + + response = handler.doGetSplits(allocator, new GetSplitsRequest(req, continuationToken)); + + logger.info("doGetSplits: continuationToken[{}] - numSplits[{}]", continuationToken, response.getSplits().size()); + + assertThat(response.getContinuationToken(), equalTo(null)); + assertThat(response.getSplits().size(), equalTo(MAX_SPLITS_PER_REQUEST)); + + logger.info("doGetSplitsQuery: exit"); + } +} diff --git a/athena-dynamodb/src/test/java/com/amazonaws/athena/connectors/dynamodb/DynamoDBRecordHandlerTest.java b/athena-dynamodb/src/test/java/com/amazonaws/athena/connectors/dynamodb/DynamoDBRecordHandlerTest.java new file mode 100644 index 0000000000..02faa0f142 --- /dev/null +++ b/athena-dynamodb/src/test/java/com/amazonaws/athena/connectors/dynamodb/DynamoDBRecordHandlerTest.java @@ -0,0 +1,210 @@ +/*- + * #%L + * athena-dynamodb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.dynamodb; + +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.amazonaws.athena.connector.lambda.data.BlockUtils; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.domain.spill.S3SpillLocation; +import com.amazonaws.athena.connector.lambda.domain.spill.SpillLocation; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsResponse; +import com.amazonaws.athena.connector.lambda.records.RecordResponse; +import com.amazonaws.athena.connector.lambda.security.EncryptionKeyFactory; +import com.amazonaws.athena.connector.lambda.security.LocalKeyFactory; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.dynamodbv2.model.AttributeValue; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.google.common.collect.ImmutableMap; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; +import java.util.UUID; + +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.EXPRESSION_NAMES_METADATA; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.EXPRESSION_VALUES_METADATA; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.HASH_KEY_NAME_METADATA; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.NON_KEY_FILTER_METADATA; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.RANGE_KEY_FILTER_METADATA; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.SEGMENT_COUNT_METADATA; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.SEGMENT_ID_PROPERTY; +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.TABLE_METADATA; +import static com.amazonaws.services.dynamodbv2.document.ItemUtils.toAttributeValue; +import static com.amazonaws.util.json.Jackson.toJsonString; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; + +public class DynamoDBRecordHandlerTest + extends TestBase +{ + + private static final Logger logger = LoggerFactory.getLogger(DynamoDBRecordHandlerTest.class); + + private static final SpillLocation SPILL_LOCATION = S3SpillLocation.newBuilder() + .withBucket(UUID.randomUUID().toString()) + .withSplitId(UUID.randomUUID().toString()) + .withQueryId(UUID.randomUUID().toString()) + .withIsDirectory(true) + .build(); + + private BlockAllocator allocator; + private EncryptionKeyFactory keyFactory = new LocalKeyFactory(); + private DynamoDBRecordHandler handler; + + @Before + public void setup() + { + allocator = new BlockAllocatorImpl(); + handler = new DynamoDBRecordHandler(ddbClient, mock(AmazonS3.class), mock(AWSSecretsManager.class), mock(AmazonAthena.class), "source_type"); + } + + @After + public void tearDown() + { + allocator.close(); + } + + @Test + public void testReadScanSplit() + throws Exception + { + logger.info("testReadScanSplit: enter"); + Map expressionNames = ImmutableMap.of("#col_6", "col_6"); + Map expressionValues = ImmutableMap.of(":v0", toAttributeValue(0), ":v1", toAttributeValue(1)); + Split split = Split.newBuilder(SPILL_LOCATION, keyFactory.create()) + .add(TABLE_METADATA, TEST_TABLE) + .add(SEGMENT_ID_PROPERTY, "0") + .add(SEGMENT_COUNT_METADATA, "1") + .add(NON_KEY_FILTER_METADATA, "NOT #col_6 IN (:v0,:v1)") + .add(EXPRESSION_NAMES_METADATA, toJsonString(expressionNames)) + .add(EXPRESSION_VALUES_METADATA, toJsonString(expressionValues)) + .build(); + + ReadRecordsRequest request = new ReadRecordsRequest( + TEST_IDENTITY, + TEST_CATALOG_NAME, + TEST_QUERY_ID, + TEST_TABLE_NAME, + schema, + split, + new Constraints(ImmutableMap.of()), + 100_000_000_000L, // too big to spill + 100_000_000_000L); + + RecordResponse rawResponse = handler.doReadRecords(allocator, request); + + assertTrue(rawResponse instanceof ReadRecordsResponse); + + ReadRecordsResponse response = (ReadRecordsResponse) rawResponse; + logger.info("testReadScanSplit: rows[{}]", response.getRecordCount()); + + assertEquals(992, response.getRecords().getRowCount()); + logger.info("testReadScanSplit: {}", BlockUtils.rowToString(response.getRecords(), 0)); + + logger.info("testReadScanSplit: exit"); + } + + @Test + public void testReadQuerySplit() + throws Exception + { + logger.info("testReadQuerySplit: enter"); + Map expressionNames = ImmutableMap.of("#col_1", "col_1"); + Map expressionValues = ImmutableMap.of(":v0", toAttributeValue(1)); + Split split = Split.newBuilder(SPILL_LOCATION, keyFactory.create()) + .add(TABLE_METADATA, TEST_TABLE) + .add(HASH_KEY_NAME_METADATA, "col_0") + .add("col_0", toJsonString(toAttributeValue("test_str_0"))) + .add(RANGE_KEY_FILTER_METADATA, "#col_1 >= :v0") + .add(EXPRESSION_NAMES_METADATA, toJsonString(expressionNames)) + .add(EXPRESSION_VALUES_METADATA, toJsonString(expressionValues)) + .build(); + + ReadRecordsRequest request = new ReadRecordsRequest( + TEST_IDENTITY, + TEST_CATALOG_NAME, + TEST_QUERY_ID, + TEST_TABLE_NAME, + schema, + split, + new Constraints(ImmutableMap.of()), + 100_000_000_000L, // too big to spill + 100_000_000_000L); + + RecordResponse rawResponse = handler.doReadRecords(allocator, request); + + assertTrue(rawResponse instanceof ReadRecordsResponse); + + ReadRecordsResponse response = (ReadRecordsResponse) rawResponse; + logger.info("testReadQuerySplit: rows[{}]", response.getRecordCount()); + + assertEquals(2, response.getRecords().getRowCount()); + logger.info("testReadQuerySplit: {}", BlockUtils.rowToString(response.getRecords(), 0)); + + logger.info("testReadQuerySplit: exit"); + } + + @Test + public void testZeroRowQuery() + throws Exception + { + logger.info("testZeroRowQuery: enter"); + Map expressionNames = ImmutableMap.of("#col_1", "col_1"); + Map expressionValues = ImmutableMap.of(":v0", toAttributeValue(1)); + Split split = Split.newBuilder(SPILL_LOCATION, keyFactory.create()) + .add(TABLE_METADATA, TEST_TABLE) + .add(HASH_KEY_NAME_METADATA, "col_0") + .add("col_0", toJsonString(toAttributeValue("test_str_999999"))) + .add(RANGE_KEY_FILTER_METADATA, "#col_1 >= :v0") + .add(EXPRESSION_NAMES_METADATA, toJsonString(expressionNames)) + .add(EXPRESSION_VALUES_METADATA, toJsonString(expressionValues)) + .build(); + + ReadRecordsRequest request = new ReadRecordsRequest( + TEST_IDENTITY, + TEST_CATALOG_NAME, + TEST_QUERY_ID, + TEST_TABLE_NAME, + schema, + split, + new Constraints(ImmutableMap.of()), + 100_000_000_000L, // too big to spill + 100_000_000_000L); + + RecordResponse rawResponse = handler.doReadRecords(allocator, request); + + assertTrue(rawResponse instanceof ReadRecordsResponse); + + ReadRecordsResponse response = (ReadRecordsResponse) rawResponse; + logger.info("testZeroRowQuery: rows[{}]", response.getRecordCount()); + + assertEquals(0, response.getRecords().getRowCount()); + + logger.info("testZeroRowQuery: exit"); + } +} diff --git a/athena-dynamodb/src/test/java/com/amazonaws/athena/connectors/dynamodb/TestBase.java b/athena-dynamodb/src/test/java/com/amazonaws/athena/connectors/dynamodb/TestBase.java new file mode 100644 index 0000000000..be497a4259 --- /dev/null +++ b/athena-dynamodb/src/test/java/com/amazonaws/athena/connectors/dynamodb/TestBase.java @@ -0,0 +1,164 @@ +/*- + * #%L + * athena-dynamodb + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.dynamodb; + +import com.amazonaws.athena.connector.lambda.ThrottlingInvoker; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.security.FederatedIdentity; +import com.amazonaws.athena.connectors.dynamodb.util.DDBTableUtils; +import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; +import com.amazonaws.services.dynamodbv2.document.DynamoDB; +import com.amazonaws.services.dynamodbv2.document.Index; +import com.amazonaws.services.dynamodbv2.document.Table; +import com.amazonaws.services.dynamodbv2.document.TableWriteItems; +import com.amazonaws.services.dynamodbv2.local.embedded.DynamoDBEmbedded; +import com.amazonaws.services.dynamodbv2.model.AttributeDefinition; +import com.amazonaws.services.dynamodbv2.model.AttributeValue; +import com.amazonaws.services.dynamodbv2.model.CreateGlobalSecondaryIndexAction; +import com.amazonaws.services.dynamodbv2.model.CreateTableRequest; +import com.amazonaws.services.dynamodbv2.model.KeySchemaElement; +import com.amazonaws.services.dynamodbv2.model.KeyType; +import com.amazonaws.services.dynamodbv2.model.Projection; +import com.amazonaws.services.dynamodbv2.model.ProjectionType; +import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput; +import com.amazonaws.services.dynamodbv2.model.ScalarAttributeType; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import org.apache.arrow.vector.types.pojo.Schema; +import org.junit.AfterClass; +import org.junit.BeforeClass; + +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +import static com.amazonaws.athena.connectors.dynamodb.constants.DynamoDBConstants.DEFAULT_SCHEMA; +import static com.amazonaws.athena.connectors.dynamodb.throttling.DynamoDBExceptionFilter.EXCEPTION_FILTER; +import static com.amazonaws.services.dynamodbv2.document.ItemUtils.toAttributeValue; +import static com.amazonaws.services.dynamodbv2.document.ItemUtils.toItem; + +public class TestBase +{ + protected FederatedIdentity TEST_IDENTITY = new FederatedIdentity("id", "principal", "account"); + protected static final String TEST_QUERY_ID = "queryId"; + protected static final String TEST_CATALOG_NAME = "default"; + protected static final String TEST_TABLE = "test_table"; + protected static final TableName TEST_TABLE_NAME = new TableName(DEFAULT_SCHEMA, TEST_TABLE); + protected static final TableName TEST_TABLE_2_NAME = new TableName(DEFAULT_SCHEMA, "Test_table2"); + + protected static AmazonDynamoDB ddbClient; + protected static Schema schema; + + @BeforeClass + public static void setupOnce() throws Exception + { + ddbClient = setupDatabase(); + ThrottlingInvoker invoker = ThrottlingInvoker.newDefaultBuilder(EXCEPTION_FILTER).build(); + schema = DDBTableUtils.peekTableForSchema(TEST_TABLE, invoker, ddbClient); + } + + @AfterClass + public static void tearDownOnce() + { + ddbClient.shutdown(); + } + + private static AmazonDynamoDB setupDatabase() throws InterruptedException + { + System.setProperty("sqlite4java.library.path", "native-libs"); + AmazonDynamoDB client = DynamoDBEmbedded.create().amazonDynamoDB(); + DynamoDB ddb = new DynamoDB(client); + + ArrayList attributeDefinitions = new ArrayList<>(); + attributeDefinitions.add(new AttributeDefinition().withAttributeName("col_0").withAttributeType("S")); + attributeDefinitions.add(new AttributeDefinition().withAttributeName("col_1").withAttributeType("N")); + + ArrayList keySchema = new ArrayList<>(); + keySchema.add(new KeySchemaElement().withAttributeName("col_0").withKeyType(KeyType.HASH)); + keySchema.add(new KeySchemaElement().withAttributeName("col_1").withKeyType(KeyType.RANGE)); + + ProvisionedThroughput provisionedThroughput = new ProvisionedThroughput() + .withReadCapacityUnits(5L) + .withWriteCapacityUnits(6L); + CreateTableRequest createTableRequest = new CreateTableRequest() + .withTableName(TEST_TABLE) + .withKeySchema(keySchema) + .withAttributeDefinitions(attributeDefinitions) + .withProvisionedThroughput(provisionedThroughput); + + Table table = ddb.createTable(createTableRequest); + + table.waitForActive(); + + TableWriteItems tableWriteItems = new TableWriteItems(TEST_TABLE); + int len = 1000; + LocalDateTime dateTime = LocalDateTime.of(2019, 9, 23, 11, 18, 37); + for (int i = 0; i < len; i++) { + Map item = new HashMap<>(); + item.put("col_0", toAttributeValue("test_str_" + (i - i % 3))); + item.put("col_1", toAttributeValue(i)); + double doubleVal = 200000.0 + i / 2.0; + if (Math.floor(doubleVal) != doubleVal) { + item.put("col_2", toAttributeValue(200000.0 + i / 2.0)); + } + item.put("col_3", toAttributeValue(ImmutableMap.of("modulo", i % 2 == 0, "nextModulos", ImmutableList.of((i + 1) % 2 == 0, ((i + 2) % 2 == 0))))); + item.put("col_4", toAttributeValue(dateTime.toLocalDate().toEpochDay())); + item.put("col_5", toAttributeValue(Timestamp.valueOf(dateTime).toInstant().toEpochMilli())); + item.put("col_6", toAttributeValue(i % 128 == 0 ? null : i % 128)); + item.put("col_7", toAttributeValue(-i)); + item.put("col_8", toAttributeValue(ImmutableSet.of(i - 100, i - 200))); + item.put("col_9", toAttributeValue(100.0f + i)); + tableWriteItems.addItemToPut(toItem(item)); + + if (tableWriteItems.getItemsToPut().size() == 25) { + ddb.batchWriteItem(tableWriteItems); + tableWriteItems = new TableWriteItems(TEST_TABLE); + } + + dateTime = dateTime.plusHours(26); + } + + CreateGlobalSecondaryIndexAction createIndexRequest = new CreateGlobalSecondaryIndexAction() + .withIndexName("test_index") + .withKeySchema( + new KeySchemaElement().withKeyType(KeyType.HASH).withAttributeName("col_4"), + new KeySchemaElement().withKeyType(KeyType.RANGE).withAttributeName("col_5")) + .withProjection(new Projection().withProjectionType(ProjectionType.ALL)) + .withProvisionedThroughput(provisionedThroughput); + Index gsi = table.createGSI(createIndexRequest, + new AttributeDefinition().withAttributeName("col_4").withAttributeType(ScalarAttributeType.N), + new AttributeDefinition().withAttributeName("col_5").withAttributeType(ScalarAttributeType.N)); + gsi.waitForActive(); + + // for case sensitivity testing + createTableRequest = new CreateTableRequest() + .withTableName("Test_table2") + .withKeySchema(keySchema) + .withAttributeDefinitions(attributeDefinitions) + .withProvisionedThroughput(provisionedThroughput); + table = ddb.createTable(createTableRequest); + table.waitForActive(); + + return client; + } +} diff --git a/athena-example/LICENSE.txt b/athena-example/LICENSE.txt new file mode 100644 index 0000000000..834d25ef58 --- /dev/null +++ b/athena-example/LICENSE.txt @@ -0,0 +1 @@ +my license diff --git a/athena-example/README.md b/athena-example/README.md new file mode 100644 index 0000000000..48a70e094f --- /dev/null +++ b/athena-example/README.md @@ -0,0 +1,193 @@ +## Example Athena Connector + +This module is meant to serve as a guided example for writing and deploying your own connector to enable Athena to query a custom source. The goal with this guided tutorial is to help you understand the development process and point out capabilities. Out of necessity some of the examples are rather contrived and make use of hard coded schemas to separate learning how to write a connector from learning how to interface with the target systems you will inevitably want to federate to. + +## What is a 'Connector'? + +A 'Connector' is a piece of code that can translate between your target data source and Athena. Today this code is expected to run in an AWS Lambda function but in the future we hope to offer more options. You can think of a connector as an extension of Athena's query engine. Athena will delegate portions of the federated query plan to your connector. More specifically: + +1. Your connector must provide a source of meta-data for Athena to get schema information about what databases, tables, and columns your connector has. This is done by building and deploying a lambda function that extends com.amazonaws.athena.connector.lambda.handlers.MetadataHandler in the athena-federation-sdk module. +2. Your connector must provide a way for Athena to read the data stored in your tables. This is done by building and deploying a lambda function that extends com.amazonaws.athena.connector.lambda.handlers.RecordHandler in the athena-federation-sdk module. + +Alternatively, you can deploy a single Lambda function which combines the two above requirements by using com.amazonaws.athena.connector.lambda.handlers.CompositeHandler or com.amazonaws.athena.connector.lambda.handlers.UnifiedHandler. While breaking this into two separate Lambda functions allows you to independently control the cost and timeout of your Lambda functions, using a single Lambda function can be simpler and higher performance due to less cold start. + +In the next section we take a closer look at the methods we must implement on the MetadataHandler and RecordHandler. + +### MetadataHandler Details + +Lets take a closer look at what is required for a MetadataHandler. Below we have the basic functions we need to implement when using the Amazon Athena Query Federation SDK's MetadataHandler to satisfy the boiler plate work of serialization and initialization. The abstract class we are extending takes care of all the Lambda interface bits and delegates on the discrete operations that are relevant to the task at hand, querying our new data source. + +```java +public class MyMetadataHandler extends MetadataHandler +{ + /** + * Used to get the list of schemas (aka databases) that this source contains. + * + * @param allocator Tool for creating and managing Apache Arrow Blocks. + * @param request Provides details on who made the request and which Athena catalog they are querying. + * @return A ListSchemasResponse which primarily contains a Set of schema names and a catalog name + * corresponding the Athena catalog that was queried. + */ + @Override + protected ListSchemasResponse doListSchemaNames(BlockAllocator allocator, ListSchemasRequest request) {} + + /** + * Used to get the list of tables that this source contains. + * + * @param allocator Tool for creating and managing Apache Arrow Blocks. + * @param request Provides details on who made the request and which Athena catalog and database they are querying. + * @return A ListTablesResponse which primarily contains a List enumerating the tables in this + * catalog, database tuple. It also contains the catalog name corresponding the Athena catalog that was queried. + */ + @Override + protected ListTablesResponse doListTables(BlockAllocator allocator, ListTablesRequest request) {} + + /** + * Used to get definition (field names, types, descriptions, etc...) of a Table. + * + * @param allocator Tool for creating and managing Apache Arrow Blocks. + * @param request Provides details on who made the request and which Athena catalog, database, and table they are querying. + * @return A GetTableResponse which primarily contains: + * 1. An Apache Arrow Schema object describing the table's columns, types, and descriptions. + * 2. A Set of partition column names (or empty if the table isn't partitioned). + */ + @Override + protected GetTableResponse doGetTable(BlockAllocator allocator, GetTableRequest request) {} + + /** + * Used to get the partitions that must be read from the request table in order to satisfy the requested predicate. + * + * @param blockWriter Used to write rows (partitions) into the Apache Arrow response. + * @param request Provides details of the catalog, database, and table being queried as well as any filter predicate. + * @note Partitions are partially opaque to Amazon Athena in that it only understands your partition columns and + * how to filter out partitions that do not meet the query's constraints. Any additional columns you add to the + * partition data are ignored by Athena but passed on to calls on GetSplits. Also note tat the BlockWriter handlers + * automatically constraining and filtering out values that don't satisfy the query's predicate. This is how we + * we accomplish partition pruning. You can optionally retreive a ConstraintEvaluator from BlockWriter if you have + * your own need to apply filtering in Lambda. Otherwise you can get the actual preducate from the request object + * for pushing down into the source you are querying. + */ + @Override + public void getPartitions(BlockWriter blockWriter, GetTableLayoutRequest request) {} + + /** + * Used to split-up the reads required to scan the requested batch of partition(s). + * + * @param allocator Tool for creating and managing Apache Arrow Blocks. + * @param request Provides details of the catalog, database, table, andpartition(s) being queried as well as + * any filter predicate. + * @return A GetSplitsResponse which primarily contains: + * 1. A Set which represent read operations Amazon Athena must perform by calling your read function. + * 2. (Optional) A continuation token which allows you to paginate the generation of splits for large queries. + * @note A Split is a mostly opaque object to Amazon Athena. Amazon Athena will use the optional SpillLocation and + * optional EncryptionKey for pipelined reads but all properties you set on the Split are passed to your read + * function to help you perform the read. + */ + @Override + protected GetSplitsResponse doGetSplits(BlockAllocator allocator, GetSplitsRequest request) {} +} +``` + +You can find example MetadataHandlers by looking at some of the connectors in the repository. athena-cloudwatch and athena-tpcds are fairly easy to follow along with. + +Alternatively, if you wish to use AWS Glue DataCatalog as the authoritative (or supplemental) source of meta-data for your connector you can extend com.amazonaws.athena.connector.lambda.handlers.GlueMetadataHandler instead of com.amazonaws.athena.connector.lambda.handlers.MetadataHandler. GlueMetadataHandler comes with implementations for doListSchemas(...), doListTables(...), and doGetTable(...) leaving you to implemented only 2 methods. The Amazon Athena DocumentDB Connector in the athena-docdb module is an example of using GlueMetadataHandler. + +### RecordHandler Details + +Lets take a closer look at what is required for a RecordHandler. Below we have the basic functions we need to implement when using the Amazon Athena Query Federation SDK's MetadataHandler to satisfy the boiler plate work of serialization and initialization. The abstract class we are extending takes care of all the Lambda interface bits and delegates on the discrete operations that are relevant to the task at hand, querying our new data source. + +```java +public class MyRecordHandler + extends RecordHandler +{ + /** + * Used to read the row data associated with the provided Split. + * @param constraints A ConstraintEvaluator capable of applying constraints form the query that request this read. + * @param spiller A BlockSpiller that should be used to write the row data associated with this Split. + * The BlockSpiller automatically handles chunking the response, encrypting, and spilling to S3. + * @param recordsRequest Details of the read request, including: + * 1. The Split + * 2. The Catalog, Database, and Table the read request is for. + * 3. The filtering predicate (if any) + * 4. The columns required for projection. + * @note Avoid writing >10 rows per-call to BlockSpiller.writeRow(...) because this will limit the BlockSpiller's + * ability to control Block size. The resulting increase in Block size may cause failures and reduced performance. + */ + @Override + protected void readWithConstraint(ConstraintEvaluator constraints, BlockSpiller spiller, ReadRecordsRequest recordsRequest){} +} +``` + +## How To Build & Deploy + +You can use any IDE or even just comman line editor to write your connector. The below steps show you how to use an AWS Cloud9 IDE running on EC2 to get started but most of the steps are applicable to any linux based development machine. + + +### Step 1: Create your Cloud9 Instance + +1. Open the AWS Console and navigate to the [Cloud9 Service or Click Here](https://console.aws.amazon.com/cloud9/) +2. Click 'Create Environment' and follow the steps to create a new instance using a new EC2 Instance (we recommend m4.large) running Amazon Linux. + + +### Step 2: Download The SDK + Connectors + +1. At your Cloud9 terminal run `git clone https://github.com/awslabs/aws-athena-query-federation.git` to get a copy of the Amazon Athena Query Federation SDK, Connector Suite, and Example Connector. + +### Step 3: Install Development Tools (Pre-Requisites) + +1. This step may be optional if you are working on a development machine that already has Apache Maven, the AWS CLI, and the AWS SAM build tool for Serverless Applications. If not, you can run the `./tools/prepare_dev_env.sh` script in the root of the github project you checked out. +2. To ensure your terminal can see the new tools we installed run `source ~/.profile` or open a fresh terminal. If you skip this step you will get errors later about the aws cli or sam build tool not being able to publish your connector. + +Now run `mvn clean install -DskipTests=true` from the athena-federation-sdk directory within the github project you checked out earlier. We are skipping tests just to make the build faster. Normally you should let the tests as a matter of best practice. + +### Step 4: Write The Code + +1. Create an s3 bucket (in the same region you will be deploying the connector), that we can use for spill and to upload some sample data using the following command `aws s3 mb s3://BUCKET_NAME` but be sure to put your actual bucket name in the command and that you pick something that is unlikely to already exist. +2. (If using Cloud9) Navigate to the aws-athena-query-federation/athena-example folder on the left nav. This is the code you extracted back in Step 2. +3. Complete the TODOs in ExampleMetadataHandler by uncommenting the provided example code and providing missing code where indicated. +4. Complete the TODOs in ExampleRecordHandler by uncommenting the provided example code and providing missing code where indicated. +5. Run the following command from the aws-athena-query-federation/athena-example directory to ensure your connector is valid. `mvn clean install` +6. Upload our sample data by running the following command from aws-athena-query-federation/athena-example directory. Be sure to replace BUCKET_NAME with the name of the bucket your created earlier. `aws s3 cp ./sample_data.csv s3://BUCKET_NAME/2017/11/1/sample_data.csv` + +### Step 5: Package and Deploy Your New Connector + +We have two options for deploying our connector: directly to Lambda or via Serverless Application Repository. We'll do both below. + +*Publish Your Connector To Serverless Application Repository* + +Run `../tools/publish.sh S3_BUCKET_NAME athena-example` to publish the connector to your private AWS Serverless Application Repository. This will allow users with permission to do so, the ability to deploy instances of the connector via 1-Click form. + +If the publish command gave you an error about the aws cli or sam tool not recognizing an argument, you likely forgot to source the new bash profile after +updating your development environment so run `source ~/.profile` and try again. + +Then you can navigate to [Serverless Application Repository](https://console.aws.amazon.com/serverlessrepo/) to search for your application and deploy it before using it from Athena. + +(Alternatively you can publish your connector directly to Lambda but for simplicity this tutorial uses Serverless Application Repository.) + +### Step 6: Validate our Connector. + +One of the most challenging aspects of integrating systems (in this case our connector and Athena) is testing how these two things will work together. Lambda will capture logging from out connector in Cloudwatch Logs but we've also tried to provide some tools to stream line detecting and correcting common semantic and logical issues with your custom connector. By running Athena's connector validation tool you can simulate how Athena will interact with your Lambda function and get access to diagnostic information that would normally only be available within Athena or require you to add extra diagnostics to your connector. + +Run `../tools/validate_connector.sh --lambda-func --schema schema1 --table table1 --constraints year=2017,month=11,day=1` +Be sure to replace lambda_func with the name you gave to your function/catalog when you deployed it via Serverless Application Repository. + +If everything worked as expected you should see the script generate useful debugging info and end with: +```txt +2019-11-07 20:25:08 <> INFO ConnectorValidator:================================================== +2019-11-07 20:25:08 <> INFO ConnectorValidator:Successfully Passed Validation! +2019-11-07 20:25:08 <> INFO ConnectorValidator:================================================== +``` + +### Step 7: Run a Query! + +Ok, now we are ready to try running some queries using our new connector. Some good examples to try include (be sure to put in your actual database and table names): + +`select * from "lambda:".schema1.table1 where year=2017 and month=11 and day=1;` + +`select transaction.completed, count(*) from "lambda:".schema1.table1 where year=2017 and month=11 and day=1 group by transaction.completed;` + +*note that the corresponds to the name of your Lambda function. + + + + diff --git a/athena-example/athena-example.yaml b/athena-example/athena-example.yaml new file mode 100644 index 0000000000..181ce69536 --- /dev/null +++ b/athena-example/athena-example.yaml @@ -0,0 +1,74 @@ +Transform: 'AWS::Serverless-2016-10-31' + +Metadata: + AWS::ServerlessRepo::Application: + Name: ExampleAthenaConnector + Description: ExampleAthenaConnector Description + Author: user1 + SpdxLicenseId: Apache-2.0 + LicenseUrl: LICENSE.txt + ReadmeUrl: README.md + Labels: ['athena-federation'] + HomePageUrl: https://github.com/awslabs/aws-athena-query-federation + SemanticVersion: 1.0.0 + SourceCodeUrl: https://github.com/awslabs/aws-athena-query-federation + +# Parameters are CloudFormation features to pass input +# to your template when you create a stack +Parameters: + AthenaCatalogName: + Description: "The name you will give to this catalog in Athena will also be used as you Lambda function name." + Type: String + SpillBucket: + Description: "The bucket where this function can spill large responses." + Type: String + DataBucket: + Description: "The bucket where this tutorial's data lives." + Type: String + SpillPrefix: + Description: "The bucket prefix where this function can spill large responses." + Type: String + Default: "athena-spill" + LambdaTimeout: + Description: "Maximum Lambda invocation runtime in seconds. (min 1 - 900 max)" + Default: 900 + Type: Number + LambdaMemory: + Description: "Lambda memory in MB (min 128 - 3008 max)." + Default: 3008 + Type: Number + DisableSpillEncryption: + Description: "WARNING: If set to 'true' encryption for spilled data is disabled." + Default: "false" + Type: String + +Resources: + ConnectorConfig: + Type: 'AWS::Serverless::Function' + Properties: + Environment: + Variables: + disable_spill_encryption: !Ref DisableSpillEncryption + spill_bucket: !Ref SpillBucket + spill_prefix: !Ref SpillPrefix + data_bucket: !Ref DataBucket + FunctionName: !Sub "${AthenaCatalogName}" + Handler: "com.amazonaws.connectors.athena.example.ExampleCompositeHandler" + CodeUri: "./target/athena-example-1.0.jar" + Description: "A guided example for writing and deploying your own federated Amazon Athena connector for a custom source." + Runtime: java8 + Timeout: !Ref LambdaTimeout + MemorySize: !Ref LambdaMemory + Policies: + - Statement: + - Action: + - athena:GetQueryExecution + Effect: Allow + Resource: '*' + Version: '2012-10-17' + #S3CrudPolicy allows our connector to spill large responses to S3. You can optionally replace this pre-made policy + #with one that is more restrictive and can only 'put' but not read,delete, or overwrite files. + - S3CrudPolicy: + BucketName: !Ref SpillBucket + - S3CrudPolicy: + BucketName: !Ref DataBucket \ No newline at end of file diff --git a/athena-example/pom.xml b/athena-example/pom.xml new file mode 100644 index 0000000000..9e22fdcad6 --- /dev/null +++ b/athena-example/pom.xml @@ -0,0 +1,65 @@ + + + + aws-athena-query-federation + com.amazonaws + 1.0 + + 4.0.0 + + athena-example + + + + com.amazonaws + aws-athena-federation-sdk + ${aws-athena-federation-sdk.version} + + + com.google.cloud + google-cloud-bigquery + 1.87.0 + compile + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.1 + + false + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + package + + shade + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + true + + + + + \ No newline at end of file diff --git a/athena-example/sample_data.csv b/athena-example/sample_data.csv new file mode 100644 index 0000000000..cccb0dd2d1 --- /dev/null +++ b/athena-example/sample_data.csv @@ -0,0 +1,11601 @@ +2017,7,18,1795956446,894609933,false +2017,7,19,1716339081,272577275,false +2017,7,19,688171731,1378122016,false +2017,7,19,193902233,1433256248,true +2017,7,19,2129370796,2049762888,true +2017,7,19,171953495,365729954,true +2017,7,19,195334229,1228390096,false +2017,7,19,1779205754,686833825,true +2017,7,19,903033383,1454168653,false +2017,7,19,1443458971,334050035,true +2017,7,19,1115189438,511247096,true +2017,7,20,2034403243,1875721250,false +2017,7,20,1955829989,444515885,true +2017,7,20,1383703271,475742027,false +2017,7,20,1868008787,17732604,true +2017,7,20,1046166032,1163070105,true +2017,7,20,1908512098,959782166,true +2017,7,20,1197601506,1009204990,true +2017,7,20,1841480979,455877752,true +2017,7,20,1203863537,1904933137,true +2017,7,20,351714297,1797721759,false +2017,7,21,1421427832,318585367,true +2017,7,21,1775258585,818080304,false +2017,7,21,756188357,1688459171,false +2017,7,21,1099288870,1882623571,true +2017,7,21,1453352512,1316701522,true +2017,7,21,1982887597,2079803704,false +2017,7,21,881896000,1491709837,false +2017,7,21,1823576827,75072892,false +2017,7,21,2000254254,1038477143,true +2017,7,21,2101582186,207247205,true +2017,7,22,1499244448,222715536,false +2017,7,22,352192992,833755954,false +2017,7,22,1078845818,802437432,false +2017,7,22,1148727703,458474483,false +2017,7,22,1584547636,305795983,true +2017,7,22,1937357967,75382435,false +2017,7,22,330637264,1973870125,false +2017,7,22,339991167,239267704,true +2017,7,22,1638559277,1288300645,true +2017,7,22,1679647568,997121957,true +2017,7,23,143676089,129379982,true +2017,7,23,725665426,501497865,false +2017,7,23,2048154282,726537479,true +2017,7,23,1994258033,655065466,true +2017,7,23,1828550729,2083861010,false +2017,7,23,1703721473,1052376490,false +2017,7,23,1007961086,412121051,true +2017,7,23,721826825,778507234,false +2017,7,23,1277093899,286541127,true +2017,7,23,1672856212,668353276,true +2017,7,24,371050820,1200582488,true +2017,7,24,1763834093,982990439,true +2017,7,24,1467624746,167216722,true +2017,7,24,730605775,2024656418,false +2017,7,24,1490344486,1052262623,true +2017,7,24,678889715,1314359781,true +2017,7,24,2097383649,1306158311,false +2017,7,24,1649956422,332512756,true +2017,7,24,253756906,298289369,true +2017,7,24,720642662,1420170328,false +2017,7,25,552184101,393933196,false +2017,7,25,1081519421,1741719400,false +2017,7,25,612588734,1995796497,true +2017,7,25,417888458,1937226474,true +2017,7,25,1201627024,1600440330,false +2017,7,25,461016451,2035068733,true +2017,7,25,2083737413,199664212,false +2017,7,25,1543172818,352183869,true +2017,7,25,704078993,1907142964,false +2017,7,25,2062877329,97653738,true +2017,7,26,938686562,1649033799,false +2017,7,26,435799966,882926838,true +2017,7,26,1856533203,191059932,true +2017,7,26,907204318,1180226502,true +2017,7,26,1564704067,945313417,false +2017,7,26,1803048762,184680587,false +2017,7,26,578701035,1740025107,true +2017,7,26,1790511237,2130123641,false +2017,7,26,266301362,2144037897,true +2017,7,26,185227860,1617344590,true +2017,7,27,387338053,1910953888,false +2017,7,27,181787419,1061251234,true +2017,7,27,743661209,733178281,true +2017,7,27,125879612,1826541241,true +2017,7,27,1871517777,1524419267,true +2017,7,27,1334313674,545459402,true +2017,7,27,180846803,1272893717,true +2017,7,27,555612706,1139938556,true +2017,7,27,1060356330,781574188,false +2017,7,27,616866994,1552874633,true +2017,7,28,591177274,550693085,true +2017,7,28,646194827,1147871429,true +2017,7,28,1887634183,704188726,true +2017,7,28,1485318274,1168848631,false +2017,7,28,665049838,1721928666,false +2017,7,28,2104092692,2123520079,false +2017,7,28,1984666789,206023529,false +2017,7,28,1201477457,1044861270,true +2017,7,28,263350499,749115839,true +2017,7,28,1009161323,31785134,false +2017,7,29,517612788,1534368815,true +2017,7,29,1692369859,571011838,false +2017,7,29,341304238,1030374654,true +2017,7,29,1730723979,962378329,true +2017,7,29,1554422209,394440661,true +2017,7,29,1205516121,1098861960,true +2017,7,29,1871456913,709409246,false +2017,7,29,933684024,149952505,false +2017,7,29,1651912995,165371845,true +2017,7,29,569516312,1254153535,true +2017,7,30,117935301,1249958619,true +2017,7,30,1385377466,173357937,true +2017,7,30,2110798698,966593538,false +2017,7,30,1060487518,865402308,false +2017,7,30,1201634311,1797721661,false +2017,7,30,793837022,1216062766,true +2017,7,30,1787633989,1885912037,false +2017,7,30,2127400705,2044010240,true +2017,7,30,565606757,228350203,false +2017,7,30,538669098,317366704,true +2017,7,31,962120243,461160983,true +2017,7,31,1607373226,614966863,false +2017,7,31,934160459,575488722,false +2017,7,31,431624544,1115029850,true +2017,7,31,1769994028,103473103,false +2017,7,31,2140122481,962119658,false +2017,7,31,1003926473,1722957730,true +2017,7,31,2018093110,164300407,true +2017,7,31,680156560,1547027276,false +2017,7,31,1467177913,2032395414,true +2017,8,1,909046311,1696504329,false +2017,8,1,717523027,343127437,false +2017,8,1,943296080,1603992981,true +2017,8,1,114740557,1853503372,false +2017,8,1,1638724268,2082085699,true +2017,8,1,1507263373,1482093541,false +2017,8,1,1103557979,755223409,true +2017,8,1,1538142652,276422824,true +2017,8,1,519496106,1997335616,false +2017,8,1,1470748258,380641371,true +2017,8,2,602504523,264785314,true +2017,8,2,780738545,513447637,false +2017,8,2,632319694,1277979655,true +2017,8,2,1491446709,1144590947,true +2017,8,2,1686479697,559811685,true +2017,8,2,378100759,626233016,false +2017,8,2,1476993446,1763374923,true +2017,8,2,713561034,51227485,false +2017,8,2,2133919582,2071192111,true +2017,8,2,1149335213,1122525596,false +2017,8,3,72108148,2043403596,false +2017,8,3,796101252,1172858395,false +2017,8,3,2066007188,1998760353,true +2017,8,3,1127743717,1910571166,false +2017,8,3,1075014962,1631059440,false +2017,8,3,1798400222,1241223343,true +2017,8,3,2115509740,29051597,false +2017,8,3,1928286786,288555034,true +2017,8,3,380140774,1250348321,true +2017,8,3,409329063,756751482,true +2017,8,4,2047298345,1209533357,false +2017,8,4,1494293361,554747733,true +2017,8,4,1650265384,549631849,false +2017,8,4,943409538,1197944946,false +2017,8,4,1784705532,120588256,false +2017,8,4,125321832,1257074796,true +2017,8,4,627076081,742335437,false +2017,8,4,2144049694,208660915,true +2017,8,4,900929401,2067911591,true +2017,8,4,713737149,734684285,true +2017,8,5,204367271,482447079,false +2017,8,5,2022408370,1847522890,true +2017,8,5,1544328634,1366937120,false +2017,8,5,795383113,1704329236,false +2017,8,5,1481436517,1871084359,true +2017,8,5,1546952635,366212670,true +2017,8,5,1407607250,1774720705,false +2017,8,5,1562625534,1870711768,false +2017,8,5,1475810192,765342475,false +2017,8,5,316738554,1011666655,false +2017,8,6,1479618037,987863576,true +2017,8,6,1518425861,972625270,true +2017,8,6,1153955479,857511613,false +2017,8,6,104578216,426466387,false +2017,8,6,2111452133,7195074,true +2017,8,6,376015387,296940750,false +2017,8,6,314087946,124941677,true +2017,8,6,370591485,1609444702,true +2017,8,6,107361183,375624617,true +2017,8,6,1227989294,1510279517,false +2017,8,7,2014302174,1144924688,false +2017,8,7,480346337,956446065,false +2017,8,7,1948774983,869627010,false +2017,8,7,696798003,573602795,false +2017,8,7,142319615,1328839192,false +2017,8,7,1914562288,1584374006,false +2017,8,7,325993810,640008582,false +2017,8,7,373897803,925175926,true +2017,8,7,1336924696,1991373140,true +2017,8,7,1955979700,441202651,false +2017,8,8,923268595,1662092656,false +2017,8,8,2014551830,1681413214,false +2017,8,8,1154858399,1011889930,true +2017,8,8,250496542,1456601693,true +2017,8,8,1441401032,1198623928,false +2017,8,8,1332905303,1700345042,true +2017,8,8,2115298340,784472146,true +2017,8,8,1057926470,1920525335,false +2017,8,8,534721989,1094614286,true +2017,8,8,401952201,885492090,false +2017,8,9,751163866,1232880566,true +2017,8,9,1944747876,301924683,true +2017,8,9,1613601888,1888359449,true +2017,8,9,1795277121,25255782,true +2017,8,9,2108887143,110096904,true +2017,8,9,1833487886,769802864,true +2017,8,9,189923326,337963377,true +2017,8,9,665876741,716050487,true +2017,8,9,124590057,47708291,false +2017,8,9,1333123508,154384892,true +2017,8,10,1485707422,1310281691,false +2017,8,10,484939884,328298893,false +2017,8,10,1902332489,527433766,false +2017,8,10,2010275418,920399988,true +2017,8,10,223462340,52507076,false +2017,8,10,157750198,1262463152,true +2017,8,10,325735620,44097100,true +2017,8,10,62522260,1453734577,true +2017,8,10,935141801,918422784,true +2017,8,10,936706094,799305346,true +2017,8,11,81677239,1329351312,false +2017,8,11,2092665799,1995062900,true +2017,8,11,450620065,1456419268,false +2017,8,11,72329598,79089573,false +2017,8,11,652096683,688745953,false +2017,8,11,1792712351,684527917,true +2017,8,11,1795721421,1298434273,true +2017,8,11,1792960595,455390441,false +2017,8,11,1283142305,1892353761,false +2017,8,11,852249292,501357833,false +2017,8,12,526369282,1731700035,true +2017,8,12,193558066,228573176,false +2017,8,12,274579615,253435779,true +2017,8,12,1242864385,1290680378,true +2017,8,12,1318321656,54285482,false +2017,8,12,1194080921,1434404933,false +2017,8,12,1737991803,771198948,false +2017,8,12,1893521966,46176907,false +2017,8,12,369194167,192766888,false +2017,8,12,545049946,1323402290,false +2017,8,13,2136576999,526240361,false +2017,8,13,1753703164,970693924,true +2017,8,13,31641919,1010591963,true +2017,8,13,210151988,370682496,true +2017,8,13,1162841534,552316187,true +2017,8,13,28251787,305606810,true +2017,8,13,2033194103,726166379,false +2017,8,13,1983238579,473634813,true +2017,8,13,582982030,589190207,true +2017,8,13,1940248168,805516602,false +2017,8,14,1061667929,569934022,false +2017,8,14,1187697482,460703081,true +2017,8,14,1600803686,697772602,true +2017,8,14,770088401,926425495,true +2017,8,14,759477083,393002395,true +2017,8,14,577207470,894081449,true +2017,8,14,1826445794,1834397860,true +2017,8,14,1217205804,137929554,false +2017,8,14,487648289,837359125,false +2017,8,14,1234643573,165241903,false +2017,8,15,195279988,1266925227,false +2017,8,15,1325253317,2047288874,true +2017,8,15,1965067617,210438490,false +2017,8,15,1208284656,1467255721,false +2017,8,15,63494133,542716074,false +2017,8,15,1589583816,652111172,true +2017,8,15,1971692054,1300509763,false +2017,8,15,314391622,297450729,false +2017,8,15,666258796,1467172105,true +2017,8,15,1599426688,982821564,true +2017,8,16,33571304,933832541,false +2017,8,16,1050622674,255927829,true +2017,8,16,1440087837,1446060632,true +2017,8,16,1845442766,1028964716,false +2017,8,16,554468828,63249440,false +2017,8,16,256294078,685541904,false +2017,8,16,1624063958,192299687,true +2017,8,16,1421680107,1797889275,true +2017,8,16,1241128248,1107322223,true +2017,8,16,1224021473,1980086312,false +2017,8,17,299802742,923451433,false +2017,8,17,2119051874,265538602,true +2017,8,17,873434810,1962981200,true +2017,8,17,706471638,1257255937,true +2017,8,17,725878718,862502665,false +2017,8,17,605253960,145344859,false +2017,8,17,48067066,1316987373,true +2017,8,17,1952671325,519051713,false +2017,8,17,1667432541,1144025228,true +2017,8,17,1581534399,1511497059,false +2017,8,18,276049180,321182666,false +2017,8,18,1855532022,2029947518,false +2017,8,18,735668534,721644392,true +2017,8,18,366556075,1039321234,true +2017,8,18,1768489658,1891579721,false +2017,8,18,975419324,617229541,false +2017,8,18,1254683636,1365988707,true +2017,8,18,21455539,696491520,false +2017,8,18,1354595344,432037724,false +2017,8,18,1688351615,151752236,true +2017,8,19,1471946193,1911495216,false +2017,8,19,349762683,673021210,false +2017,8,19,1594784618,2053420937,true +2017,8,19,1921460518,269184825,false +2017,8,19,977243811,1372924807,true +2017,8,19,73024800,396596588,true +2017,8,19,1630438091,1785854755,false +2017,8,19,520104515,1235962388,false +2017,8,19,1830226472,1233684099,true +2017,8,19,1497283828,296231089,true +2017,8,20,1656290015,154433479,false +2017,8,20,429630712,722377610,true +2017,8,20,1208835987,1080654400,false +2017,8,20,226778747,1009794840,false +2017,8,20,25331236,60555029,true +2017,8,20,1075405322,125680932,true +2017,8,20,436405079,323120053,true +2017,8,20,1136571302,852419033,true +2017,8,20,2068622420,89058225,true +2017,8,20,1273587779,1350654791,false +2017,8,21,1196375859,132387536,false +2017,8,21,1729033807,1912434681,false +2017,8,21,1745971734,142267770,true +2017,8,21,2085634317,617075629,false +2017,8,21,872686719,1574169808,true +2017,8,21,1705545717,362352986,true +2017,8,21,1423274213,1012388105,true +2017,8,21,1094113868,1315178500,true +2017,8,21,1886235829,1622859751,false +2017,8,21,488000696,645385223,false +2017,8,22,1433805632,1258051180,true +2017,8,22,1553432957,765423844,false +2017,8,22,1182315587,1050758160,false +2017,8,22,335928728,610328509,false +2017,8,22,328898835,2072324900,true +2017,8,22,1652601869,506428663,true +2017,8,22,1926151056,658572590,true +2017,8,22,19695083,1603799997,true +2017,8,22,369965158,2029382492,false +2017,8,22,923386107,627083363,true +2017,8,23,2057280077,1007481446,true +2017,8,23,2099318812,702261969,false +2017,8,23,982363717,2010386028,true +2017,8,23,1737783216,1895233660,true +2017,8,23,1368035582,792809150,true +2017,8,23,1292658091,1886507421,true +2017,8,23,887182319,2006418063,true +2017,8,23,2123522078,878790568,false +2017,8,23,1880909000,748603127,true +2017,8,23,152266337,568908585,true +2017,8,24,1959414850,2028026848,false +2017,8,24,804993049,1687948690,false +2017,8,24,1294496125,362672296,true +2017,8,24,714726099,2026114987,true +2017,8,24,31188327,423187099,true +2017,8,24,771076595,1595625363,false +2017,8,24,153017830,103798428,true +2017,8,24,1463262746,716001690,true +2017,8,24,471665338,404982929,true +2017,8,24,1243787355,627746142,false +2017,8,25,1420451408,63149677,true +2017,8,25,749624017,1211170032,false +2017,8,25,55563004,853700076,false +2017,8,25,767982454,171965931,true +2017,8,25,1905537370,663243617,false +2017,8,25,1805612879,767648958,false +2017,8,25,2026337981,2072390949,true +2017,8,25,1391635263,1169363109,true +2017,8,25,141378301,1140703877,false +2017,8,25,1097796902,302226167,true +2017,8,26,2033466115,673588803,true +2017,8,26,672580380,1097713367,true +2017,8,26,1158128092,773698568,true +2017,8,26,417990620,2037870198,false +2017,8,26,1895242816,1067493718,false +2017,8,26,121420482,859318612,true +2017,8,26,1101247182,1761246683,true +2017,8,26,1546307305,178834257,true +2017,8,26,1051018611,1295401012,false +2017,8,26,1207038027,8167015,false +2017,8,27,723046058,1759484806,false +2017,8,27,1679351538,2055268706,true +2017,8,27,816160669,1258997618,true +2017,8,27,129253270,1753711610,true +2017,8,27,1366373971,638277452,false +2017,8,27,33412676,205649170,false +2017,8,27,343999847,4716198,true +2017,8,27,1710111454,563573258,true +2017,8,27,246253827,1883271299,true +2017,8,27,155318419,376970857,true +2017,8,28,1020782353,25834459,true +2017,8,28,1868349598,1183953227,true +2017,8,28,1122773212,1223799598,true +2017,8,28,163159540,1006058067,true +2017,8,28,909535902,346672724,true +2017,8,28,509831844,211333655,true +2017,8,28,944060845,1525202140,true +2017,8,28,1765572888,1343513141,true +2017,8,28,1735545918,1125645446,false +2017,8,28,1939640948,1763217309,true +2017,8,29,1535911425,289696453,false +2017,8,29,1020133579,1575959985,false +2017,8,29,136069884,455620944,false +2017,8,29,1091888864,1449126727,true +2017,8,29,269735534,1841474850,false +2017,8,29,27682988,1578033846,false +2017,8,29,857214377,1341906045,false +2017,8,29,1415696101,1187621996,true +2017,8,29,1573586716,1690090445,false +2017,8,29,473079087,303200203,true +2017,8,30,887507976,1017055869,false +2017,8,30,1969034696,689336375,true +2017,8,30,550135700,88860698,false +2017,8,30,568379088,905839772,false +2017,8,30,1758156859,276518751,false +2017,8,30,1778883698,68859351,true +2017,8,30,838758604,452495349,false +2017,8,30,1502468250,402660550,false +2017,8,30,772691054,1020639506,false +2017,8,30,1361579016,278467305,false +2017,8,31,540220784,1297856061,true +2017,8,31,1491144152,1017991391,false +2017,8,31,1799684291,1541055709,true +2017,8,31,501859791,238943267,false +2017,8,31,296743026,1317004384,false +2017,8,31,437762841,1960481226,true +2017,8,31,960982397,1426355228,false +2017,8,31,2085325887,545058370,true +2017,8,31,1191638484,734755128,false +2017,8,31,418713194,1438118458,true +2017,9,1,909694320,799894589,true +2017,9,1,299147621,1506083637,false +2017,9,1,570010037,1276901669,true +2017,9,1,1442224848,1775334020,false +2017,9,1,55651496,1721763323,true +2017,9,1,1292523134,1533362059,false +2017,9,1,497217947,162493291,true +2017,9,1,1205324548,1819001513,false +2017,9,1,1912418137,1437945817,true +2017,9,1,700401874,2126980122,true +2017,9,2,296588288,1556951246,false +2017,9,2,471046003,378480291,false +2017,9,2,381746439,273319906,false +2017,9,2,366004174,1302351858,false +2017,9,2,394451487,1712185196,false +2017,9,2,1656036213,1211820272,false +2017,9,2,374049782,1003243679,false +2017,9,2,310255717,1654800002,false +2017,9,2,1347241984,1106163675,true +2017,9,2,799335884,1727006558,false +2017,9,3,328696021,1879500121,true +2017,9,3,528009641,1866558984,true +2017,9,3,1671927490,1413633923,true +2017,9,3,117055584,255985174,false +2017,9,3,1091581606,1050202512,false +2017,9,3,1062973093,766212670,false +2017,9,3,1259887913,1314212925,false +2017,9,3,1508633402,128521451,false +2017,9,3,448266993,1460481980,false +2017,9,3,111350141,472774467,true +2017,9,4,1011815016,930843949,false +2017,9,4,303072959,1101591267,true +2017,9,4,679412265,1560929091,false +2017,9,4,801489865,1902516423,true +2017,9,4,1303861041,309812445,false +2017,9,4,1743071534,298073095,false +2017,9,4,244840543,1692254651,false +2017,9,4,1710469167,1121153403,true +2017,9,4,1017360634,872501404,false +2017,9,4,593664860,954469736,false +2017,9,5,1776488998,936951650,true +2017,9,5,494727487,571889336,true +2017,9,5,742990737,351041813,true +2017,9,5,552073390,1763191667,true +2017,9,5,280793542,1752249050,false +2017,9,5,1648357780,1417458799,false +2017,9,5,1464423651,1848698931,false +2017,9,5,491138647,1310463606,true +2017,9,5,1759491237,1703840809,true +2017,9,5,2062831912,1556503889,false +2017,9,6,1020864265,198316084,false +2017,9,6,276043220,1847703922,false +2017,9,6,31218930,128129777,true +2017,9,6,1358662647,75244351,false +2017,9,6,1961751700,1180547084,true +2017,9,6,1252804261,994221860,true +2017,9,6,2098744211,13352940,true +2017,9,6,1758436824,1628652532,false +2017,9,6,1585699610,2128838909,false +2017,9,6,1118501709,535091534,false +2017,9,7,1085994578,104179115,false +2017,9,7,1282613144,2107700721,false +2017,9,7,105103361,1342454132,false +2017,9,7,280208781,312018507,true +2017,9,7,1316308547,1808751760,true +2017,9,7,11376985,743973290,true +2017,9,7,1557134220,626227494,true +2017,9,7,271781341,515580115,true +2017,9,7,1913965177,386659849,false +2017,9,7,480380458,1739428591,true +2017,9,8,1896535587,1241334379,true +2017,9,8,209459755,1219495435,false +2017,9,8,2054957801,293337871,false +2017,9,8,44877030,1108160157,true +2017,9,8,1711424488,901031131,true +2017,9,8,1266849752,758504912,false +2017,9,8,1110501278,1803114975,true +2017,9,8,1722905170,1623521414,false +2017,9,8,873499255,1553964327,false +2017,9,8,785949935,1160106309,true +2017,9,9,1869656096,1901635319,true +2017,9,9,442001522,1668347293,true +2017,9,9,192003324,964668986,false +2017,9,9,248550568,1350155163,false +2017,9,9,1324277534,901991763,false +2017,9,9,409919276,1896167014,false +2017,9,9,1783249532,1509814160,true +2017,9,9,1354656541,1051246881,false +2017,9,9,1412752995,21727721,true +2017,9,9,250341921,1188140445,true +2017,9,10,1528994396,1225034966,false +2017,9,10,254364748,230291596,true +2017,9,10,1063696120,221531747,true +2017,9,10,4706248,2026174246,true +2017,9,10,2037494583,732885700,true +2017,9,10,1552841293,1325699720,false +2017,9,10,1444835733,1695311644,false +2017,9,10,1243248075,1158419254,false +2017,9,10,304981982,1607804215,false +2017,9,10,912572117,1991846783,false +2017,9,11,2087775054,324894813,false +2017,9,11,1972952743,740956607,false +2017,9,11,885597872,13499026,false +2017,9,11,26762218,742405033,true +2017,9,11,1345770107,680322777,true +2017,9,11,801698943,1914157601,false +2017,9,11,800737052,1022834775,true +2017,9,11,1526521832,1131780437,true +2017,9,11,61636584,1667571559,true +2017,9,11,1616780608,1494178574,true +2017,9,12,283947379,773967105,false +2017,9,12,2020833461,1276256630,true +2017,9,12,1906098238,265597439,false +2017,9,12,1214796796,733187774,false +2017,9,12,1351679220,2104206116,false +2017,9,12,973185601,459741809,false +2017,9,12,1826169266,897565687,true +2017,9,12,1385350016,1986989909,true +2017,9,12,836202514,160942536,true +2017,9,12,671960129,1731286310,false +2017,9,13,1392284140,739204479,true +2017,9,13,1054744119,1468780434,true +2017,9,13,864849672,1389948418,false +2017,9,13,1554567981,1827967037,true +2017,9,13,1872451643,972191322,false +2017,9,13,1087254339,1456008121,true +2017,9,13,545399968,1501651413,false +2017,9,13,2089445495,1514717619,true +2017,9,13,1297463287,986326494,true +2017,9,13,707354841,1349355202,true +2017,9,14,393195840,1667705928,true +2017,9,14,1345832461,1174858404,false +2017,9,14,100714294,3904047,false +2017,9,14,988558302,1228464067,false +2017,9,14,1371630646,927063753,false +2017,9,14,2052606225,1116954971,true +2017,9,14,1585661363,1637841592,false +2017,9,14,171041984,1616784284,true +2017,9,14,1256538950,52112321,true +2017,9,14,205836248,215989656,false +2017,9,15,777682939,1212185067,false +2017,9,15,1355725207,2134179553,false +2017,9,15,1548321905,73038456,false +2017,9,15,151910177,1818678048,false +2017,9,15,2050853593,500062778,true +2017,9,15,1181726076,464344077,true +2017,9,15,1202789895,306115627,false +2017,9,15,277779095,1016686219,false +2017,9,15,1296261892,1553865301,false +2017,9,15,310816041,791181796,true +2017,9,16,1207804143,1208141419,false +2017,9,16,452557961,976390218,false +2017,9,16,1351269640,1014725797,true +2017,9,16,1549334738,2136372777,true +2017,9,16,173502774,1604687545,true +2017,9,16,686233833,1693893112,true +2017,9,16,1189646707,242892297,false +2017,9,16,1450327875,643934338,true +2017,9,16,1156632791,2059101023,false +2017,9,16,930621315,314147299,false +2017,9,17,1423225153,1704611792,false +2017,9,17,1120147003,1531492776,false +2017,9,17,130068682,2027441554,false +2017,9,17,1659945342,560492762,false +2017,9,17,350875273,477343991,true +2017,9,17,589198344,1325998804,true +2017,9,17,1421767760,1820920006,true +2017,9,17,125279768,629972536,false +2017,9,17,723101032,817611428,true +2017,9,17,1454124643,1332402534,false +2017,9,18,715133565,1434118993,false +2017,9,18,380304030,124628529,false +2017,9,18,1684582702,932131811,true +2017,9,18,1174012558,420997846,false +2017,9,18,86000134,298179652,true +2017,9,18,1421722027,1829467740,false +2017,9,18,317594056,1614864543,false +2017,9,18,1829102252,2020676832,true +2017,9,18,581081933,1182112389,false +2017,9,18,1879887266,817280130,false +2017,9,19,639239534,2050965476,false +2017,9,19,1126663265,931309036,false +2017,9,19,1189600427,431429937,false +2017,9,19,1976918637,1165085606,true +2017,9,19,1917172366,1018117460,false +2017,9,19,1687317675,1473982908,false +2017,9,19,128761085,78769457,true +2017,9,19,757807735,28226390,false +2017,9,19,1886633464,2128910823,true +2017,9,19,1711076607,1024452185,false +2017,9,20,818018418,1096091675,false +2017,9,20,1639826152,951551280,true +2017,9,20,1144609099,522530792,true +2017,9,20,1239352744,145788223,true +2017,9,20,1672250259,645479500,true +2017,9,20,1101620000,559109304,true +2017,9,20,575215007,1415951916,false +2017,9,20,1291747676,1452246216,true +2017,9,20,426315924,1542669891,true +2017,9,20,532343671,1061764441,true +2017,9,21,1782576756,120315541,true +2017,9,21,1323212320,1892695511,true +2017,9,21,499536602,633493312,false +2017,9,21,1165002547,1132450489,true +2017,9,21,1517125851,1488645062,true +2017,9,21,1466083166,711526853,true +2017,9,21,1764601037,179789085,false +2017,9,21,2028260810,769613634,true +2017,9,21,814799587,493165096,true +2017,9,21,2127291716,1424704657,true +2017,9,22,31601631,2096923500,true +2017,9,22,32362329,1796316398,true +2017,9,22,389254983,42825758,true +2017,9,22,1567466847,1203185475,false +2017,9,22,1325020893,319531475,false +2017,9,22,978532453,1492767826,true +2017,9,22,1084539222,1104349572,true +2017,9,22,910270585,58642081,true +2017,9,22,937407094,858114980,true +2017,9,22,374597841,629968366,true +2017,9,23,652074274,1548178030,false +2017,9,23,405944752,157696534,false +2017,9,23,691560457,863802560,true +2017,9,23,2013567519,516309428,true +2017,9,23,1057755117,968336671,false +2017,9,23,2125943512,828671711,true +2017,9,23,1844857524,2067439534,true +2017,9,23,144601323,1982116054,false +2017,9,23,5772496,1680985927,false +2017,9,23,277197330,1678510968,false +2017,9,24,1866339994,1034819831,true +2017,9,24,433952166,142005717,true +2017,9,24,1812996526,1045037698,true +2017,9,24,2077242541,550434631,true +2017,9,24,1107846772,1313653701,false +2017,9,24,777334927,844338888,true +2017,9,24,567148633,780211214,true +2017,9,24,1479887677,1064184455,false +2017,9,24,1009257307,458726332,true +2017,9,24,224355575,206546409,false +2017,9,25,1356177410,314535883,true +2017,9,25,539654906,1103624822,false +2017,9,25,367979221,584028184,true +2017,9,25,625813430,1985952869,true +2017,9,25,821471974,124410489,false +2017,9,25,868933724,1055782338,true +2017,9,25,1212663582,997463048,false +2017,9,25,1072893160,703634277,true +2017,9,25,1291185207,1701207853,true +2017,9,25,1587343092,696728882,false +2017,9,26,1929598392,384270409,true +2017,9,26,434465005,343441241,true +2017,9,26,1328124489,1822622740,false +2017,9,26,913238669,911162513,true +2017,9,26,579555382,873759239,true +2017,9,26,115826848,581038469,true +2017,9,26,986028287,349250157,false +2017,9,26,68212099,70701025,true +2017,9,26,1903061828,248189681,true +2017,9,26,1434305751,1968645070,false +2017,9,27,1637083252,115768769,false +2017,9,27,615895139,2056265917,true +2017,9,27,1468293115,746763931,false +2017,9,27,1400886556,1873363653,true +2017,9,27,1522398190,2135848548,true +2017,9,27,1432757956,50055182,true +2017,9,27,1073836900,1803745560,true +2017,9,27,86328746,1299802613,false +2017,9,27,1948509439,1432417073,true +2017,9,27,1046906213,1221066738,true +2017,9,28,1744119883,1654811352,true +2017,9,28,45229226,1357719146,false +2017,9,28,1220517629,1210965634,true +2017,9,28,1947211144,1046016489,false +2017,9,28,1772095302,429364672,false +2017,9,28,1438546607,1315779458,false +2017,9,28,1543513765,2115818094,true +2017,9,28,1384675727,524512578,false +2017,9,28,2097719557,1438558088,true +2017,9,28,1097908847,1584225784,true +2017,9,29,1747204619,2072891463,false +2017,9,29,1800922836,323064261,false +2017,9,29,1300262620,1249496409,false +2017,9,29,1746375620,399030574,true +2017,9,29,1419846854,802234195,false +2017,9,29,1011275982,525548195,false +2017,9,29,748459406,1345420455,false +2017,9,29,1604328931,1867879099,false +2017,9,29,1976900697,783396553,false +2017,9,29,527702370,661796251,true +2017,9,30,1875414620,917841228,false +2017,9,30,1018449116,532205621,true +2017,9,30,14856420,850302735,false +2017,9,30,444013190,1619300973,true +2017,9,30,1547881039,815007974,true +2017,9,30,85243474,1773183809,false +2017,9,30,1239227915,673027635,false +2017,9,30,652336412,2145752708,true +2017,9,30,1347249090,1245262706,false +2017,9,30,206023035,390774744,false +2017,9,31,2141423655,789160872,false +2017,9,31,1053876494,137896159,true +2017,9,31,1709199923,1919209770,false +2017,9,31,221773403,637698268,false +2017,9,31,1143697836,1073084227,false +2017,9,31,1571740326,1786481217,true +2017,9,31,1434129483,390618524,true +2017,9,31,253460541,1926735579,true +2017,9,31,566818011,1375674229,false +2017,9,31,1965369648,2060340633,true +2017,10,1,219151403,223504395,false +2017,10,1,1462898546,38706466,true +2017,10,1,1786613161,107021001,true +2017,10,1,1101089490,1781007540,true +2017,10,1,296454649,1608116491,false +2017,10,1,2061751886,993489158,false +2017,10,1,1169981423,1366419617,false +2017,10,1,1579338155,366904610,true +2017,10,1,414089388,1368941912,false +2017,10,1,431369568,139519855,true +2017,10,2,1604724179,1390929197,true +2017,10,2,1567383083,1530931558,false +2017,10,2,963748116,1887263223,true +2017,10,2,2075921137,547445926,false +2017,10,2,1790554742,1644119212,false +2017,10,2,745947827,355439202,true +2017,10,2,3553069,1864150416,true +2017,10,2,1120934336,720329312,false +2017,10,2,380056799,756404711,false +2017,10,2,945747054,599485697,false +2017,10,3,161408421,1796023050,false +2017,10,3,575394556,1952452619,false +2017,10,3,880928552,2059932651,false +2017,10,3,508747118,241633318,false +2017,10,3,282090411,1413155176,true +2017,10,3,491796354,388634968,true +2017,10,3,1985287656,64151302,true +2017,10,3,1731896330,2038351573,true +2017,10,3,470409467,2059314404,true +2017,10,3,777127000,851394313,false +2017,10,4,1447832544,1143788805,false +2017,10,4,280173431,1369815713,true +2017,10,4,977396775,141364554,true +2017,10,4,1588887145,1695555068,false +2017,10,4,209235433,1714991276,false +2017,10,4,2048987558,386396758,false +2017,10,4,1113257768,1046314391,false +2017,10,4,1108554115,584638585,false +2017,10,4,839332637,1205500022,true +2017,10,4,81688157,2005354727,false +2017,10,5,1478092325,373051602,false +2017,10,5,1638678159,494826936,false +2017,10,5,1906746693,1709110415,true +2017,10,5,449157017,487506074,true +2017,10,5,1852167299,12593194,true +2017,10,5,1751782671,1177023415,false +2017,10,5,1732466052,1774584847,true +2017,10,5,1677881123,2000336498,true +2017,10,5,1204259886,2133653073,false +2017,10,5,714388446,162533179,true +2017,10,6,423735618,2011016568,false +2017,10,6,1028734321,1576171998,false +2017,10,6,2010475302,449047577,false +2017,10,6,1819790627,1577506339,false +2017,10,6,324707162,1526007410,true +2017,10,6,938274239,1235883168,true +2017,10,6,1887279948,328106572,true +2017,10,6,1686923670,434378741,true +2017,10,6,2069753223,251875013,true +2017,10,6,1302789998,1711886079,true +2017,10,7,791707759,358489297,false +2017,10,7,1328670455,830417059,true +2017,10,7,581764620,405854872,true +2017,10,7,1932914845,1625766961,true +2017,10,7,235505810,1600193276,false +2017,10,7,1325100492,1621158302,true +2017,10,7,1350256035,1190963176,true +2017,10,7,1106667014,1109908227,true +2017,10,7,645712513,1435543862,false +2017,10,7,743193178,2047952158,true +2017,10,8,1225159081,537049932,false +2017,10,8,1046907931,357799627,true +2017,10,8,335814292,750553038,true +2017,10,8,346969319,676956080,true +2017,10,8,2035084936,1298964456,false +2017,10,8,1925868897,1274459280,false +2017,10,8,1745264066,1196423966,false +2017,10,8,490509614,1918452736,true +2017,10,8,92612196,1553376943,false +2017,10,8,317405709,473495671,true +2017,10,9,1526837993,1667819746,true +2017,10,9,1055001891,159100137,true +2017,10,9,654569482,1127334109,true +2017,10,9,134397149,569118577,false +2017,10,9,2081630812,531113685,true +2017,10,9,508966633,1840746939,true +2017,10,9,2090205717,1197674932,false +2017,10,9,937392440,633964835,false +2017,10,9,2108156846,1284759182,true +2017,10,9,1742635536,523004108,false +2017,10,10,1792196194,358060918,false +2017,10,10,828714633,1728015894,false +2017,10,10,511920574,1773103770,false +2017,10,10,658185236,610473026,true +2017,10,10,1893523895,229862308,true +2017,10,10,2013314265,1656536608,true +2017,10,10,1473189124,2027120858,true +2017,10,10,2046693394,154750239,true +2017,10,10,2107630511,1248971381,false +2017,10,10,1085646508,1953325843,false +2017,10,11,1571609627,622067230,true +2017,10,11,220273101,2072699021,false +2017,10,11,975114986,1987277209,true +2017,10,11,592882718,1811899300,false +2017,10,11,2115307151,904038147,true +2017,10,11,692045793,1376004972,false +2017,10,11,1394456946,1985953328,false +2017,10,11,310524733,1310810782,true +2017,10,11,200324584,1213411850,true +2017,10,11,1655228742,91062298,true +2017,10,12,1199953264,2061062661,false +2017,10,12,576631209,1496102472,true +2017,10,12,1082628343,684080890,true +2017,10,12,1858241032,258957955,false +2017,10,12,1706897936,1941092368,false +2017,10,12,34712426,1755030547,false +2017,10,12,1976769888,2078793592,false +2017,10,12,1581531974,1099679337,false +2017,10,12,13561009,1825349675,false +2017,10,12,197712658,1019645065,true +2017,10,13,1598860136,1078523108,true +2017,10,13,516342376,772848628,true +2017,10,13,456241746,658476563,false +2017,10,13,1814177556,627643250,true +2017,10,13,782030849,1593714551,true +2017,10,13,1567337381,1230845565,false +2017,10,13,943800633,656938914,true +2017,10,13,1360267363,1063424072,true +2017,10,13,74299668,1257902746,false +2017,10,13,991259573,1177606551,true +2017,10,14,729545446,1871187531,false +2017,10,14,57042511,1501778654,true +2017,10,14,2110441486,427459209,false +2017,10,14,1612549330,304871520,true +2017,10,14,1434161719,489959383,true +2017,10,14,832007068,759334988,true +2017,10,14,1512898222,186646313,true +2017,10,14,1693008629,576533254,false +2017,10,14,380772829,1997257310,false +2017,10,14,1894715620,925813789,false +2017,10,15,131209742,1784815655,true +2017,10,15,1432945487,1090412189,true +2017,10,15,1869821694,804814729,false +2017,10,15,919033903,2081272253,false +2017,10,15,831139823,709518930,true +2017,10,15,1253655302,1066574624,false +2017,10,15,1878694956,1840468743,false +2017,10,15,1777235409,321704477,true +2017,10,15,889127497,1156748857,false +2017,10,15,1191164209,968096709,false +2017,10,16,842592390,2034364493,false +2017,10,16,130245252,413215144,false +2017,10,16,551337235,1836240904,true +2017,10,16,528597014,122062214,false +2017,10,16,376461101,853062546,true +2017,10,16,193237702,861393515,false +2017,10,16,427035216,1777787040,true +2017,10,16,1594732963,381437933,true +2017,10,16,727304938,1031520778,false +2017,10,16,1702612852,791504512,true +2017,10,17,1319741531,31033335,true +2017,10,17,308960043,1406428191,true +2017,10,17,1592123934,917011809,true +2017,10,17,1820421031,587175612,false +2017,10,17,965734004,1042788527,true +2017,10,17,1758782562,319608364,false +2017,10,17,1844580074,1857642108,false +2017,10,17,469521359,437698969,true +2017,10,17,1092283274,705484025,false +2017,10,17,1592182171,2014721174,false +2017,10,18,868067231,1323012972,true +2017,10,18,1816900111,1872467558,false +2017,10,18,1983908683,1772424363,true +2017,10,18,1238018368,607984160,false +2017,10,18,1841841000,479132851,true +2017,10,18,682238936,911151277,true +2017,10,18,113143146,2043183264,false +2017,10,18,1722832019,53290457,true +2017,10,18,1246574390,1745249723,true +2017,10,18,1431944393,782850021,true +2017,10,19,889543472,265764689,false +2017,10,19,104133540,250461544,false +2017,10,19,1996898029,1435964563,true +2017,10,19,1082348230,102649690,false +2017,10,19,1634895687,781954738,false +2017,10,19,1479959698,1371602330,true +2017,10,19,1065459405,1643908332,false +2017,10,19,1894436695,1137500761,true +2017,10,19,2008155074,709171022,true +2017,10,19,830823942,601982249,true +2017,10,20,1181108515,1623368275,false +2017,10,20,1152221763,1301305504,true +2017,10,20,1218845296,176358566,false +2017,10,20,761498861,775685583,true +2017,10,20,934990896,50542001,true +2017,10,20,686731629,210321157,true +2017,10,20,270889826,1143830215,false +2017,10,20,804259189,275629964,false +2017,10,20,2078952538,1305623041,true +2017,10,20,249684463,771600485,true +2017,10,21,475149755,1692737834,true +2017,10,21,1339401034,1237944884,true +2017,10,21,1322642,1121639504,false +2017,10,21,1279891061,1562839760,true +2017,10,21,1821183672,2146579152,false +2017,10,21,1811690771,1093169468,false +2017,10,21,1886590725,764799738,false +2017,10,21,1384414939,946167026,true +2017,10,21,1045646080,1839162270,true +2017,10,21,1759940391,941417925,true +2017,10,22,697298951,1003280419,true +2017,10,22,2028098826,413079493,true +2017,10,22,552562221,1923236599,false +2017,10,22,403497365,1106877162,true +2017,10,22,1556983100,1272050548,true +2017,10,22,443867920,2022559862,false +2017,10,22,2080896154,1561396120,false +2017,10,22,1428417875,1346838599,false +2017,10,22,685498120,1431058200,true +2017,10,22,371781247,597630612,false +2017,10,23,1393044336,2100379833,false +2017,10,23,1092547781,1257680586,true +2017,10,23,1057794618,98976759,false +2017,10,23,786826504,488825472,true +2017,10,23,1046170348,55736417,false +2017,10,23,1544782706,1696668606,false +2017,10,23,799338485,954034481,false +2017,10,23,704865880,16678929,false +2017,10,23,96091891,1579824905,true +2017,10,23,1564174594,1168973334,false +2017,10,24,21213663,1385616093,false +2017,10,24,1774946098,457628039,false +2017,10,24,1520486872,1476866164,true +2017,10,24,778208124,806375926,true +2017,10,24,917503628,1464220675,false +2017,10,24,693037518,205067954,true +2017,10,24,1906240876,1105969614,false +2017,10,24,492516542,180528776,false +2017,10,24,318435885,1083359192,true +2017,10,24,866797425,2118943843,false +2017,10,25,451397192,1400346884,false +2017,10,25,1172152124,153887229,false +2017,10,25,170760053,826517049,false +2017,10,25,878921549,80091156,true +2017,10,25,544687447,448494947,false +2017,10,25,532350461,1678845922,false +2017,10,25,1827714252,446187370,false +2017,10,25,232406683,2123391955,true +2017,10,25,682390439,2028041588,false +2017,10,25,332393427,537505189,true +2017,10,26,469684664,1011049232,true +2017,10,26,2001122807,1935738485,true +2017,10,26,1636183153,88000023,false +2017,10,26,752668122,61081960,true +2017,10,26,1425997960,22251116,false +2017,10,26,847753212,2091312120,false +2017,10,26,1896642918,288130783,true +2017,10,26,1940297012,1488209669,true +2017,10,26,1776367683,417291738,false +2017,10,26,612363553,1620750904,false +2017,10,27,1696136509,1898366893,false +2017,10,27,1857362947,473771874,false +2017,10,27,775637712,609619338,true +2017,10,27,1845602514,408266198,true +2017,10,27,1575295098,611419024,true +2017,10,27,418865575,949513719,false +2017,10,27,1740096359,1712909693,true +2017,10,27,951356370,1514687334,true +2017,10,27,1573172352,824040668,false +2017,10,27,247614010,474448029,false +2017,10,28,799422776,1422214339,true +2017,10,28,1747177425,1292753112,false +2017,10,28,1535172826,1208049288,false +2017,10,28,692585800,1869045145,true +2017,10,28,409395802,1234296819,true +2017,10,28,1686229065,1671856937,false +2017,10,28,1847439047,2017738446,true +2017,10,28,1561032660,945617897,false +2017,10,28,33649398,1973264107,false +2017,10,28,535397534,1205707851,true +2017,10,29,381351684,1440407294,true +2017,10,29,1664370056,3746254,false +2017,10,29,1375311028,1245795786,false +2017,10,29,557686646,575061330,false +2017,10,29,1777333726,650773666,false +2017,10,29,256202043,1992711578,true +2017,10,29,60916326,1319673131,false +2017,10,29,1584942934,219062496,true +2017,10,29,733866359,431785944,true +2017,10,29,500231337,1601399996,true +2017,10,30,335909876,1482845604,false +2017,10,30,344841342,1746686418,false +2017,10,30,171843160,443839212,false +2017,10,30,1285209966,1234851081,true +2017,10,30,1196541575,8626814,false +2017,10,30,1647288327,2112315948,true +2017,10,30,566498872,1866518325,true +2017,10,30,208244036,37389389,true +2017,10,30,1501859066,1385873334,false +2017,10,30,2114195514,557833795,true +2017,10,31,596423948,52206386,true +2017,10,31,399977342,2036484626,true +2017,10,31,1369470078,1230346036,false +2017,10,31,1972100659,1631981610,true +2017,10,31,209690704,867345892,true +2017,10,31,200742341,1551065008,true +2017,10,31,1771596172,93759818,true +2017,10,31,2032885943,708749150,true +2017,10,31,910179476,2121616238,false +2017,10,31,1055973905,778744470,false +2017,11,1,505981200,1930517617,false +2017,11,1,283822162,1007292597,false +2017,11,1,1045834,1300778732,false +2017,11,1,1357722603,580639960,false +2017,11,1,1559275988,144271423,false +2017,11,1,1706481845,705432196,true +2017,11,1,1360247502,2040941969,false +2017,11,1,1144064737,1260713977,true +2017,11,1,347114026,627249068,false +2017,11,1,159327054,142786832,false +2017,11,2,816505527,1655867141,false +2017,11,2,356288586,1426044410,false +2017,11,2,1472646622,1992230534,false +2017,11,2,1186856745,954924856,false +2017,11,2,904779357,176879357,false +2017,11,2,980912188,104918179,false +2017,11,2,1278310491,1856380466,false +2017,11,2,721533447,911818592,false +2017,11,2,459226027,640547867,true +2017,11,2,1739041227,571473681,false +2017,11,3,1520946973,1508775333,true +2017,11,3,1630912189,1675995297,false +2017,11,3,26903591,497158152,true +2017,11,3,1264099972,1197724032,false +2017,11,3,1210077271,679618723,true +2017,11,3,1827515599,1319846959,true +2017,11,3,1905724991,1820660944,true +2017,11,3,1992882473,232993627,false +2017,11,3,109095397,699873846,true +2017,11,3,1360239949,2057893642,true +2017,11,4,1060062423,2029837182,true +2017,11,4,319980462,899869570,false +2017,11,4,1116454004,1712109170,false +2017,11,4,1062605038,575474665,false +2017,11,4,893348112,1982859256,false +2017,11,4,593970292,86233819,false +2017,11,4,1118041840,94406503,false +2017,11,4,868828010,198859108,false +2017,11,4,1344270938,800843293,true +2017,11,4,731415236,724019484,false +2017,11,5,333878830,1519237479,false +2017,11,5,782426141,174608480,false +2017,11,5,2143006909,369181725,false +2017,11,5,1324825231,301174743,true +2017,11,5,1572771035,1335536250,true +2017,11,5,1711690714,37584576,false +2017,11,5,1783242084,887522840,true +2017,11,5,624050021,1570985927,true +2017,11,5,1603557284,558162206,true +2017,11,5,1489188876,2112770634,true +2017,11,6,1376421226,151791354,true +2017,11,6,1013427892,1367936090,false +2017,11,6,82928040,10348100,true +2017,11,6,703501075,2067802072,false +2017,11,6,494729962,1202463382,true +2017,11,6,1215073602,2138597759,false +2017,11,6,1519230235,2144190087,true +2017,11,6,1638066654,278065856,true +2017,11,6,146946114,2092172546,false +2017,11,6,1922477037,160311217,true +2017,11,7,1183188027,2125258415,true +2017,11,7,1513250501,767690885,true +2017,11,7,1514398119,1985780531,true +2017,11,7,1381642145,1774144068,true +2017,11,7,1202894456,637581158,false +2017,11,7,1072781738,1514593471,true +2017,11,7,583717102,1590788313,false +2017,11,7,1628264602,1417993200,true +2017,11,7,1207223776,1622881338,false +2017,11,7,364344655,237943112,true +2017,11,8,1408849817,1889929084,false +2017,11,8,1768056028,59557276,true +2017,11,8,597838025,258918867,true +2017,11,8,1585950202,1214693033,true +2017,11,8,1979425569,723268312,true +2017,11,8,283159024,1287170924,false +2017,11,8,1630632897,1781919606,false +2017,11,8,247467335,1262467268,true +2017,11,8,1349037956,1424344694,true +2017,11,8,1310409185,387972109,false +2017,11,9,1881800635,64364474,false +2017,11,9,704401229,1947571138,true +2017,11,9,822244366,2017894539,true +2017,11,9,1825892214,1703059008,true +2017,11,9,1903526003,489405577,true +2017,11,9,1606302965,1157397824,false +2017,11,9,126105196,261807637,false +2017,11,9,1223548337,433973414,true +2017,11,9,497504034,1160606298,true +2017,11,9,892450711,1266154933,true +2017,11,10,641347567,242910260,true +2017,11,10,622984840,1122357195,true +2017,11,10,2080161128,257095341,false +2017,11,10,2000794070,1036949031,true +2017,11,10,502657309,1195879111,true +2017,11,10,176311523,406877813,false +2017,11,10,921584758,1126448583,true +2017,11,10,720277950,633777679,true +2017,11,10,1331113854,1344496712,true +2017,11,10,1685497303,1472891950,true +2017,11,11,1835779196,862684079,true +2017,11,11,1403893788,1002429834,true +2017,11,11,93383076,709943016,true +2017,11,11,573664545,2065638928,true +2017,11,11,848062546,758498410,false +2017,11,11,1171073309,1013833665,true +2017,11,11,1175391612,892697326,true +2017,11,11,1964527845,1667104209,false +2017,11,11,1423243626,96108403,false +2017,11,11,1722126890,1580775834,true +2017,11,12,652807381,1202180685,true +2017,11,12,1392148647,938951439,true +2017,11,12,578662390,1974264445,true +2017,11,12,51390723,2108678304,true +2017,11,12,304221231,140438917,false +2017,11,12,373158794,240937693,true +2017,11,12,1569901398,2141042541,true +2017,11,12,257761306,1389653553,true +2017,11,12,664246214,1716396477,false +2017,11,12,1824168393,840542606,false +2017,11,13,1772715716,1228321170,true +2017,11,13,942320690,1065396967,false +2017,11,13,694946526,2094093341,true +2017,11,13,453219700,1766589120,false +2017,11,13,1902968387,1668609575,false +2017,11,13,708564793,304184904,true +2017,11,13,1760695632,318807665,true +2017,11,13,1688458860,518265773,true +2017,11,13,616413125,669121548,false +2017,11,13,503946339,59805361,true +2017,11,14,1390611286,311529723,false +2017,11,14,2106897825,1693334446,true +2017,11,14,482280604,1465851584,true +2017,11,14,998246791,593763712,true +2017,11,14,2067393427,1452772761,false +2017,11,14,892496778,594290449,false +2017,11,14,306044707,766083234,true +2017,11,14,1215062462,1069280129,true +2017,11,14,1235830630,377223192,true +2017,11,14,1516994021,242073629,true +2017,11,15,412444845,1653151849,true +2017,11,15,969964858,1420865948,false +2017,11,15,965376369,988428248,true +2017,11,15,320700593,507615843,false +2017,11,15,2078034862,837223905,false +2017,11,15,1524294245,2018900413,false +2017,11,15,26793572,1516947105,true +2017,11,15,1220724328,1628367943,true +2017,11,15,1090012713,906791400,false +2017,11,15,1961514042,452224003,false +2017,11,16,319538191,1866713284,false +2017,11,16,1107417986,607029966,false +2017,11,16,1903388761,911031720,false +2017,11,16,537317864,1812243500,false +2017,11,16,1257169576,153938550,true +2017,11,16,1050208551,1600707497,true +2017,11,16,936956836,932996374,true +2017,11,16,64570277,734118720,true +2017,11,16,774878297,12025449,false +2017,11,16,1220827956,588966275,false +2017,11,17,204801469,658929836,true +2017,11,17,1940279504,455307645,false +2017,11,17,1190948622,960174509,true +2017,11,17,534865265,1584203400,true +2017,11,17,123206680,159203939,true +2017,11,17,224926538,1492612228,true +2017,11,17,318760061,904014022,true +2017,11,17,1927955472,1652140343,false +2017,11,17,1860549523,819341812,false +2017,11,17,840293398,384007618,true +2017,11,18,738372726,408500749,false +2017,11,18,1103319913,2093182825,false +2017,11,18,1079773282,1216842831,true +2017,11,18,481795376,1150743840,true +2017,11,18,1640103277,820443468,true +2017,11,18,1902467829,887452577,true +2017,11,18,1312447035,1683700316,true +2017,11,18,474975403,1050575855,false +2017,11,18,983621446,965875862,false +2017,11,18,201500552,692150422,true +2017,11,19,1420515524,1848736497,false +2017,11,19,187278606,694250510,true +2017,11,19,1266197600,95789668,true +2017,11,19,177328339,584249087,false +2017,11,19,2058185786,483470356,true +2017,11,19,1376102128,1678964574,true +2017,11,19,58316005,909865206,true +2017,11,19,2079475226,1535162782,false +2017,11,19,1174548067,836689778,true +2017,11,19,1477353426,856722279,true +2017,11,20,168228944,1978124008,false +2017,11,20,509869618,578720656,true +2017,11,20,727893941,2020478496,true +2017,11,20,787380797,964635980,true +2017,11,20,804710719,1911571731,true +2017,11,20,495773375,970837203,false +2017,11,20,903447752,1777647505,false +2017,11,20,230904605,927039980,false +2017,11,20,1979490171,2126756263,true +2017,11,20,1359212203,360452377,true +2017,11,21,669986810,1606107096,true +2017,11,21,2073006172,1859229274,true +2017,11,21,1833868357,1756333190,true +2017,11,21,412272617,1482804428,true +2017,11,21,1809120097,1248772383,false +2017,11,21,321713347,241695872,true +2017,11,21,273704164,1496485091,true +2017,11,21,1934933100,1082999906,true +2017,11,21,1696204534,1804010599,false +2017,11,21,2098605325,2103707663,false +2017,11,22,1563735916,2106393628,true +2017,11,22,818557538,73386467,false +2017,11,22,185454335,1945396610,true +2017,11,22,2143392333,1381971882,false +2017,11,22,1720673745,1991733716,true +2017,11,22,1042660278,1185332408,false +2017,11,22,921636541,206366286,true +2017,11,22,51060098,1408767571,true +2017,11,22,198837036,1201849685,false +2017,11,22,297848005,818560950,true +2017,11,23,1756423000,330044723,false +2017,11,23,931842169,1718558998,false +2017,11,23,541511500,1773561893,true +2017,11,23,705394272,1548210539,false +2017,11,23,1622555399,519236320,true +2017,11,23,485480419,1592425716,true +2017,11,23,1596034176,1513131495,true +2017,11,23,1873526530,260518568,true +2017,11,23,1218829444,1498020674,false +2017,11,23,1003539126,1658554642,true +2017,11,24,1152265124,43531636,true +2017,11,24,454981459,1821132623,false +2017,11,24,161977008,469341637,true +2017,11,24,621996745,1255381283,true +2017,11,24,1060207211,2081935162,true +2017,11,24,1883547101,877190495,false +2017,11,24,792021122,1176023247,false +2017,11,24,1012644523,1280533523,false +2017,11,24,1608007660,1050270932,true +2017,11,24,1025369613,1167951421,true +2017,11,25,1936793909,1199357050,true +2017,11,25,1227472068,1510666997,true +2017,11,25,1556444015,841080299,true +2017,11,25,1874941550,1169223237,true +2017,11,25,2028198066,1291434129,true +2017,11,25,209276847,1708530735,true +2017,11,25,519076217,1884614221,true +2017,11,25,1236529697,376004242,false +2017,11,25,1038415932,966994381,true +2017,11,25,746429399,935581413,true +2017,11,26,1978176277,1723758923,false +2017,11,26,256143776,1103385866,true +2017,11,26,66628443,1755040337,false +2017,11,26,775212624,1776612003,true +2017,11,26,1128983398,1297073752,false +2017,11,26,131731124,331636397,true +2017,11,26,157630730,1079710051,false +2017,11,26,716840185,1572405070,true +2017,11,26,1308421995,1495835360,false +2017,11,26,191154100,854958110,false +2017,11,27,1768203556,845475437,true +2017,11,27,762269647,236351821,false +2017,11,27,1816976401,1193452902,false +2017,11,27,547028479,2136848498,false +2017,11,27,1743019533,2088850229,true +2017,11,27,631950228,1671253492,false +2017,11,27,976662143,1428145633,true +2017,11,27,581536701,1665919545,false +2017,11,27,218159773,567175512,true +2017,11,27,63330720,1650584625,true +2017,11,28,812901021,1241532248,true +2017,11,28,1789963474,120239820,false +2017,11,28,1116675154,902926092,true +2017,11,28,594946311,1774687610,true +2017,11,28,670084070,61025980,true +2017,11,28,1427724067,594302934,true +2017,11,28,794429878,1899001623,true +2017,11,28,896976109,1673073398,false +2017,11,28,1308726384,632275718,true +2017,11,28,196861453,137347876,false +2017,11,29,678884621,842860554,false +2017,11,29,1445198886,1120891626,false +2017,11,29,1585491514,1818088081,false +2017,11,29,2134506786,462169265,false +2017,11,29,629089092,842782403,false +2017,11,29,1185543698,2009436775,true +2017,11,29,1678885557,155564474,true +2017,11,29,1125361191,734853869,false +2017,11,29,1624399638,1720080419,false +2017,11,29,1887236279,168900403,false +2017,11,30,1920019605,2125489846,false +2017,11,30,1142894155,1418805582,true +2017,11,30,1837687937,1637819916,true +2017,11,30,266036255,2111882741,true +2017,11,30,622938074,355129012,false +2017,11,30,1600829097,452220186,true +2017,11,30,430518377,390067102,true +2017,11,30,1871808245,2001658020,false +2017,11,30,1816568857,814204979,false +2017,11,30,1667623936,937918579,false +2017,11,31,13664394,1184238770,false +2017,11,31,1464103003,798718258,true +2017,11,31,1280009488,440245926,false +2017,11,31,1170766257,519002020,false +2017,11,31,570625955,2055961751,true +2017,11,31,997610255,2044994676,false +2017,11,31,154565994,1886237510,false +2017,11,31,2033896442,751850667,false +2017,11,31,118184417,543176716,true +2017,11,31,1820102078,829143381,false +2018,1,1,1321447376,673025847,false +2018,1,1,227802482,2073713927,true +2018,1,1,1290663306,473425534,false +2018,1,1,889425063,2017691708,true +2018,1,1,947225137,1333906142,false +2018,1,1,1070140357,976428484,false +2018,1,1,1740753899,282994307,false +2018,1,1,1388034164,1918285499,false +2018,1,1,1263701842,267226754,true +2018,1,1,179220401,456878532,false +2018,1,2,1626521220,790391190,false +2018,1,2,1906699861,2058521465,false +2018,1,2,201581208,1732833314,false +2018,1,2,984642624,741606992,false +2018,1,2,701957233,1490141973,false +2018,1,2,1299004767,1336718946,true +2018,1,2,433736213,2082187926,false +2018,1,2,735897927,1213895135,false +2018,1,2,1439550492,399394425,false +2018,1,2,160359716,175825165,false +2018,1,3,1075628754,377547241,true +2018,1,3,1046482207,756511013,false +2018,1,3,454552865,1468059861,true +2018,1,3,1644220819,1613566830,true +2018,1,3,355115869,821831926,false +2018,1,3,1492797859,776072799,false +2018,1,3,411154050,1443846545,false +2018,1,3,2010255378,688306365,false +2018,1,3,1085245587,1063672897,true +2018,1,3,108241003,154662622,false +2018,1,4,1543972108,1421289063,true +2018,1,4,422050266,1344819366,false +2018,1,4,1235814574,1967515160,true +2018,1,4,2042094152,755704531,false +2018,1,4,471469578,59997855,false +2018,1,4,524160915,1956518066,false +2018,1,4,814235794,958074859,true +2018,1,4,124915199,1981084506,false +2018,1,4,643566120,1768339847,true +2018,1,4,2028847664,1450588661,true +2018,1,5,462638932,79561960,false +2018,1,5,48378441,979399369,false +2018,1,5,599010474,1744487477,false +2018,1,5,301901068,1522222842,false +2018,1,5,1779315724,622496219,true +2018,1,5,1502244619,750756667,false +2018,1,5,1995100878,1617132161,true +2018,1,5,1482081606,5641708,true +2018,1,5,937624029,1252060740,true +2018,1,5,1433203154,207054679,false +2018,1,6,1889802110,589197118,false +2018,1,6,1029055066,439108358,false +2018,1,6,1035834946,1357574842,true +2018,1,6,53348754,2016203110,false +2018,1,6,770422587,637075384,true +2018,1,6,1282900993,1619978045,true +2018,1,6,991378843,1956028544,false +2018,1,6,1606593952,671810709,false +2018,1,6,1439395910,672058995,false +2018,1,6,368847178,1366773547,true +2018,1,7,1455616504,997740134,false +2018,1,7,195367918,3216519,false +2018,1,7,987179792,1381499170,true +2018,1,7,103746809,342212159,false +2018,1,7,1768628261,977293026,false +2018,1,7,1326854790,568037151,true +2018,1,7,527988452,878697957,false +2018,1,7,1424997153,1748535844,true +2018,1,7,710811382,1019741038,false +2018,1,7,385632329,439196812,false +2018,1,8,55387859,135898056,true +2018,1,8,116651005,255859311,true +2018,1,8,473875668,2024624421,false +2018,1,8,867420393,1769595286,true +2018,1,8,1164122849,413317752,true +2018,1,8,1070241811,1282316429,true +2018,1,8,720553865,770880153,false +2018,1,8,1575569443,1085509671,false +2018,1,8,1723695470,1163302216,false +2018,1,8,442603916,872963051,true +2018,1,9,683201096,2084922103,false +2018,1,9,1217413186,1612993053,false +2018,1,9,2057182439,136963428,true +2018,1,9,1524887246,1055678055,true +2018,1,9,602701312,1183997535,true +2018,1,9,495177681,61078095,true +2018,1,9,180836782,1661990740,true +2018,1,9,24363611,1906984183,false +2018,1,9,2078709731,116798333,false +2018,1,9,328452768,867903454,false +2018,1,10,1704083526,1064720309,false +2018,1,10,1053842415,1147521176,false +2018,1,10,668754481,873652844,true +2018,1,10,468826619,1251100606,false +2018,1,10,2122682308,1407720243,true +2018,1,10,866025172,1581091706,true +2018,1,10,2027993931,1269974220,true +2018,1,10,1365157925,227713156,true +2018,1,10,408521842,569075146,false +2018,1,10,504183177,1120773738,false +2018,1,11,1450596084,915585825,true +2018,1,11,409195291,594498674,false +2018,1,11,1027493573,1080626493,false +2018,1,11,154165907,716470912,false +2018,1,11,1304065514,999234416,false +2018,1,11,344302215,271091136,false +2018,1,11,1209048705,2024239831,false +2018,1,11,1390755491,1559925513,true +2018,1,11,1951647654,1312955669,false +2018,1,11,1845591298,264440109,true +2018,1,12,724280875,1683419234,false +2018,1,12,162335721,1059365815,true +2018,1,12,1950442095,470859239,false +2018,1,12,763648143,1663259335,false +2018,1,12,1072958128,1033860197,false +2018,1,12,282881720,1086910216,false +2018,1,12,1520565669,997088398,true +2018,1,12,1520509524,1392150775,false +2018,1,12,1364407123,231483810,true +2018,1,12,2014182236,1577033972,false +2018,1,13,1809046092,1903199268,false +2018,1,13,1440962971,692120016,true +2018,1,13,593481180,1465599439,false +2018,1,13,271669559,1445980635,false +2018,1,13,690095576,1118283019,false +2018,1,13,996899337,747695769,false +2018,1,13,1604508415,1411210131,false +2018,1,13,236436258,1389487534,false +2018,1,13,1775493944,1391277838,true +2018,1,13,911216925,807066009,false +2018,1,14,1074188043,1525730244,false +2018,1,14,1027713283,268409692,false +2018,1,14,1629496869,1303314620,true +2018,1,14,717724396,689478179,true +2018,1,14,1455985364,163787232,false +2018,1,14,657544875,1868406911,true +2018,1,14,1182575789,1114800129,false +2018,1,14,883196444,1978020577,false +2018,1,14,2012770531,1394337738,false +2018,1,14,551551987,155069768,true +2018,1,15,608756475,555397126,false +2018,1,15,1009639198,1298861669,false +2018,1,15,763050716,298319046,true +2018,1,15,1651253782,1881648296,false +2018,1,15,1277205865,472229962,true +2018,1,15,68997808,1167388141,false +2018,1,15,692728623,747715511,true +2018,1,15,1508457041,475665601,true +2018,1,15,1048679624,1977536769,true +2018,1,15,465541011,1334140535,false +2018,1,16,1328187139,511122584,true +2018,1,16,44683705,1338807180,false +2018,1,16,1192620271,631613613,true +2018,1,16,926459784,637460486,true +2018,1,16,1870357599,1089060152,true +2018,1,16,463914588,1000823756,true +2018,1,16,1714064770,1428707068,false +2018,1,16,415338385,1490496307,true +2018,1,16,1951135899,1951153466,true +2018,1,16,185524636,380870194,true +2018,1,17,1154999557,806127841,true +2018,1,17,268848998,754467063,true +2018,1,17,686351111,180689228,true +2018,1,17,936156104,81656530,false +2018,1,17,980471329,473453046,true +2018,1,17,458711094,1846161591,false +2018,1,17,247130074,425746855,true +2018,1,17,1160727804,1391851206,false +2018,1,17,1495430965,587206416,true +2018,1,17,14007968,1937490065,false +2018,1,18,1357311837,1459677128,true +2018,1,18,1176520507,1401897505,true +2018,1,18,2073381921,221087268,true +2018,1,18,924685296,277373926,false +2018,1,18,1457626456,1590869870,true +2018,1,18,94606794,445635095,false +2018,1,18,963151,576700250,false +2018,1,18,1016158878,1817062441,false +2018,1,18,2102856893,1210232531,false +2018,1,18,2132237776,2026394474,false +2018,1,19,1162856194,1223316336,true +2018,1,19,1971362309,2118557670,true +2018,1,19,1889631124,1860921902,true +2018,1,19,586423340,1211226392,true +2018,1,19,1282771939,1398680401,true +2018,1,19,1530565985,985768982,true +2018,1,19,617607975,978499177,false +2018,1,19,1509637270,945269570,false +2018,1,19,934489546,2105863413,true +2018,1,19,334400011,479847330,false +2018,1,20,556808303,2146815776,false +2018,1,20,705218041,155724384,true +2018,1,20,35606986,286495100,true +2018,1,20,1950898424,1535009077,true +2018,1,20,1085116606,126200794,false +2018,1,20,1918900239,1047247524,true +2018,1,20,2000262271,2079318150,false +2018,1,20,1262597999,454893383,true +2018,1,20,694167213,2031157388,true +2018,1,20,1787491902,1376652204,false +2018,1,21,59090844,601345238,false +2018,1,21,635781898,2104953447,true +2018,1,21,1688148809,1421022538,true +2018,1,21,603806643,1786810476,false +2018,1,21,1088272816,1187573287,true +2018,1,21,1191318768,871207369,true +2018,1,21,168398713,997445968,false +2018,1,21,2095568062,1656959834,true +2018,1,21,456004664,1240568910,true +2018,1,21,1296805970,1299094032,true +2018,1,22,59322803,1916179761,false +2018,1,22,792025453,248681179,true +2018,1,22,2070420459,58470289,false +2018,1,22,1012623395,516142685,false +2018,1,22,1116412896,1608695902,false +2018,1,22,1658905964,928600558,true +2018,1,22,551755585,325651015,true +2018,1,22,968726556,850776984,false +2018,1,22,1339166092,1924791770,true +2018,1,22,333072667,499927319,true +2018,1,23,861249350,103387057,true +2018,1,23,300305647,1420739071,false +2018,1,23,2073513555,223720508,true +2018,1,23,1524166641,19899279,false +2018,1,23,1263664798,1040948247,true +2018,1,23,1624651722,268847340,false +2018,1,23,1936573576,1833317606,true +2018,1,23,587110417,824521898,false +2018,1,23,1258144472,2012910119,true +2018,1,23,1117915408,578334955,false +2018,1,24,180685863,453740165,true +2018,1,24,1833645866,682791305,false +2018,1,24,651351031,1643065738,false +2018,1,24,271814228,127987708,false +2018,1,24,1111529981,1750656712,false +2018,1,24,333351060,1621605121,false +2018,1,24,1558079039,1456892616,true +2018,1,24,398274454,1073275941,false +2018,1,24,1301886742,1971937125,false +2018,1,24,588972200,1257595850,true +2018,1,25,711031357,1510124639,false +2018,1,25,1169532865,102123581,false +2018,1,25,128802919,756877897,true +2018,1,25,724640097,13901548,true +2018,1,25,1513299738,1151493406,true +2018,1,25,2114655333,853233396,false +2018,1,25,83842749,994304286,false +2018,1,25,849964081,899416679,true +2018,1,25,1007477537,1736030151,false +2018,1,25,1020377266,1935544358,false +2018,1,26,644213478,992228355,true +2018,1,26,1064201509,467044898,false +2018,1,26,1413357198,1266457573,true +2018,1,26,490999733,732050529,false +2018,1,26,476533278,988816264,false +2018,1,26,1222613820,162187031,true +2018,1,26,644023449,438429675,true +2018,1,26,1555499478,1579453108,true +2018,1,26,1876135613,1178515944,true +2018,1,26,400099844,1233528508,false +2018,1,27,451454659,1671364750,false +2018,1,27,1886294417,1273525434,true +2018,1,27,570308894,1790607242,false +2018,1,27,1654335607,1813143221,true +2018,1,27,144660065,183445861,true +2018,1,27,138654580,1098077416,true +2018,1,27,1588442323,290498234,true +2018,1,27,1331235093,1969440952,false +2018,1,27,1872826470,651735742,true +2018,1,27,1880032602,1563061011,true +2018,1,28,666377735,350300836,true +2018,1,28,1915416387,17676992,false +2018,1,28,732028790,1900904643,false +2018,1,28,1577527526,689687365,false +2018,1,28,1049918259,440499602,false +2018,1,28,125181410,600483702,false +2018,1,28,2006736936,885761998,false +2018,1,28,1744602435,1872452290,true +2018,1,28,1327562501,1399890535,false +2018,1,28,1271538483,1571295964,true +2018,1,29,1576326042,252058122,false +2018,1,29,1003525146,1971912533,true +2018,1,29,1086678669,1684327772,true +2018,1,29,1392451051,1641260859,false +2018,1,29,1558277179,1516020299,false +2018,1,29,1408785876,550333664,false +2018,1,29,1680523178,169863162,false +2018,1,29,189249427,553711184,true +2018,1,29,157728981,1506125871,true +2018,1,29,1451688210,1407139108,true +2018,1,30,205530037,1073321047,false +2018,1,30,1395445794,1032827571,true +2018,1,30,700682713,369326453,false +2018,1,30,392585578,1781677517,true +2018,1,30,627978860,1519029141,false +2018,1,30,1414010589,86273416,true +2018,1,30,486493424,747771037,true +2018,1,30,1816746339,730800322,false +2018,1,30,2112968993,916353944,false +2018,1,30,223218798,1856021609,false +2018,1,31,1588787903,300527810,false +2018,1,31,365169587,1337588395,true +2018,1,31,714420772,340166663,true +2018,1,31,1976511044,1837968274,true +2018,1,31,643072762,649952948,false +2018,1,31,68431800,1187134403,true +2018,1,31,196708462,1171829382,true +2018,1,31,38480683,962347087,false +2018,1,31,243823276,604331866,true +2018,1,31,448558388,387387104,false +2018,2,1,1139002255,1603530290,true +2018,2,1,419475755,1202114467,true +2018,2,1,226930885,824478535,false +2018,2,1,1399544654,1464289984,false +2018,2,1,325451738,1242305117,true +2018,2,1,210683046,284343611,true +2018,2,1,1907543269,1402890138,true +2018,2,1,569841349,1483497878,false +2018,2,1,806778574,1453613195,true +2018,2,1,977594541,2011575954,true +2018,2,2,1466487912,1841693723,false +2018,2,2,1069900663,769915605,true +2018,2,2,1290918856,1837199789,false +2018,2,2,2082586787,256794213,false +2018,2,2,1558960562,1256980116,false +2018,2,2,1744788754,963388220,true +2018,2,2,537616616,1857576604,true +2018,2,2,994018069,206429274,true +2018,2,2,429627836,419546989,false +2018,2,2,112638745,103956258,false +2018,2,3,497358913,408567769,true +2018,2,3,1409856984,60955884,false +2018,2,3,1549462496,2058912651,false +2018,2,3,1055067162,2083334916,true +2018,2,3,837600581,2005950596,false +2018,2,3,1665593189,57953690,false +2018,2,3,1822484449,520947562,false +2018,2,3,1696255022,762263117,false +2018,2,3,669495919,1066320027,false +2018,2,3,1865455985,450715984,false +2018,2,4,1615523390,221882233,false +2018,2,4,1785033394,993846373,false +2018,2,4,1473402675,351257204,false +2018,2,4,592048337,627222806,true +2018,2,4,539885241,1533663670,true +2018,2,4,448320324,115449852,false +2018,2,4,702470691,173350781,false +2018,2,4,779665037,2138754296,true +2018,2,4,1826228515,344646795,false +2018,2,4,1222995457,884198552,false +2018,2,5,1031011,1798275122,true +2018,2,5,1766950466,836841781,false +2018,2,5,1178739818,594548456,true +2018,2,5,1696762621,79553118,true +2018,2,5,187315023,378491744,true +2018,2,5,614999485,1080038315,false +2018,2,5,440981477,678970785,false +2018,2,5,1196702978,1716682782,true +2018,2,5,1336283498,1159643289,false +2018,2,5,1057825238,101182851,false +2018,2,6,1372054238,1614121482,false +2018,2,6,30058382,414031777,false +2018,2,6,1616324490,773706231,true +2018,2,6,6036618,1489298713,false +2018,2,6,1741631073,1720155601,false +2018,2,6,266019847,1961711671,true +2018,2,6,1368296770,1051668756,false +2018,2,6,1444360285,1361665071,false +2018,2,6,96916804,392392732,true +2018,2,6,1955668545,1667292027,true +2018,2,7,685787024,3478346,true +2018,2,7,926821614,1431758418,true +2018,2,7,1864134264,2121093091,false +2018,2,7,202235955,1772918889,true +2018,2,7,668213449,2099982632,false +2018,2,7,65007835,2137863052,true +2018,2,7,1248593295,1439123344,true +2018,2,7,893605109,114208932,true +2018,2,7,583974081,1796465973,true +2018,2,7,823090392,1292942155,true +2018,2,8,1793682829,610092500,true +2018,2,8,885517053,937896961,false +2018,2,8,229782847,854838761,false +2018,2,8,107781131,649147281,false +2018,2,8,762239476,2041376717,true +2018,2,8,63825307,1930139224,false +2018,2,8,1028901536,330862313,true +2018,2,8,1683406250,913973342,true +2018,2,8,1061284362,1776400470,false +2018,2,8,1125289395,1050538891,false +2018,2,9,1412330698,436505986,false +2018,2,9,978901976,8783707,true +2018,2,9,1536725614,1582307502,false +2018,2,9,2015265209,1373320663,true +2018,2,9,2045796436,1920124008,true +2018,2,9,29445052,205984712,true +2018,2,9,1181454555,489301580,true +2018,2,9,649184498,282960475,true +2018,2,9,1961075699,901450415,true +2018,2,9,835228388,1381906088,false +2018,2,10,1098143291,1693647362,true +2018,2,10,1878223337,1499713510,true +2018,2,10,1778432552,477129227,true +2018,2,10,1853955545,915148587,true +2018,2,10,294687472,1711135825,true +2018,2,10,808842288,1082993670,true +2018,2,10,1626573609,161505348,true +2018,2,10,1530393603,323299164,false +2018,2,10,963498334,1265142539,true +2018,2,10,1108790617,1252602446,true +2018,2,11,1824658189,1805854717,false +2018,2,11,1765401791,1207783692,true +2018,2,11,278121149,1723919408,true +2018,2,11,498100532,795787751,false +2018,2,11,1414918203,137520294,false +2018,2,11,509077951,615788286,false +2018,2,11,1038377501,723489282,true +2018,2,11,465827147,884292954,false +2018,2,11,1613260308,933023167,false +2018,2,11,1212725922,1357854293,false +2018,2,12,817131963,792294506,true +2018,2,12,845721498,173629596,true +2018,2,12,804954080,1240118607,true +2018,2,12,73715156,307999802,false +2018,2,12,2113459843,1593680784,false +2018,2,12,573849748,1100216667,false +2018,2,12,488259575,457213349,true +2018,2,12,1265975975,1410180050,true +2018,2,12,1978099174,1308833717,false +2018,2,12,858829907,183561608,true +2018,2,13,796165937,570528333,false +2018,2,13,681930930,49700959,false +2018,2,13,1947974109,1771502164,true +2018,2,13,1603399454,1451834406,false +2018,2,13,1441308815,1147590741,false +2018,2,13,2119833707,1258323893,true +2018,2,13,1594621758,1155499129,true +2018,2,13,171292961,1818420306,true +2018,2,13,958651691,1672120770,false +2018,2,13,1291874560,1383882976,false +2018,2,14,899822143,1217515106,true +2018,2,14,667860328,757456530,false +2018,2,14,1142484061,64127053,false +2018,2,14,605196155,594754324,false +2018,2,14,157447497,323972180,true +2018,2,14,1986795549,669881442,true +2018,2,14,169172015,533263203,false +2018,2,14,97903382,877443285,true +2018,2,14,585592318,1833104575,true +2018,2,14,2132561923,1059226109,false +2018,2,15,14085636,1158942044,true +2018,2,15,402375387,792030055,true +2018,2,15,65655949,1915112342,true +2018,2,15,66311986,238546537,false +2018,2,15,420252548,1348090874,false +2018,2,15,2139834496,975348345,false +2018,2,15,1740677430,1439813352,true +2018,2,15,553938709,1797645766,false +2018,2,15,1772414923,1060788261,true +2018,2,15,2056713346,745174029,true +2018,2,16,395019289,973250755,false +2018,2,16,471075745,717218264,false +2018,2,16,1047468242,1276379366,false +2018,2,16,1735907200,854393003,false +2018,2,16,1548325699,2104592971,true +2018,2,16,341774808,484232402,true +2018,2,16,287290653,275629474,false +2018,2,16,1692089562,1291730886,true +2018,2,16,1966758651,1171993622,false +2018,2,16,1152060797,249907813,true +2018,2,17,2030518695,2046032792,false +2018,2,17,18813579,48095343,true +2018,2,17,1313848394,226340810,false +2018,2,17,1034172953,2131325705,true +2018,2,17,1911356427,265575069,true +2018,2,17,931433027,2127891001,true +2018,2,17,1063994825,1913556730,true +2018,2,17,674331439,710372747,false +2018,2,17,164842740,297778454,false +2018,2,17,1743237065,1548225132,false +2018,2,18,199357103,1686635325,false +2018,2,18,509059004,1383109336,true +2018,2,18,1747545798,1918672694,true +2018,2,18,1865281679,678181230,false +2018,2,18,1083014171,530760592,false +2018,2,18,950930674,921372681,true +2018,2,18,458757262,1135753594,false +2018,2,18,1824313494,522232942,false +2018,2,18,801810590,732279535,false +2018,2,18,1703685173,1892285103,true +2018,2,19,1542266152,1749897826,true +2018,2,19,826624133,400705362,true +2018,2,19,1416119820,1654883374,true +2018,2,19,825768021,1981737825,false +2018,2,19,992779951,1725244273,true +2018,2,19,1715964166,456844004,true +2018,2,19,821737861,1300879134,true +2018,2,19,582829582,1192931971,false +2018,2,19,96196138,448261480,true +2018,2,19,153493344,77314872,true +2018,2,20,377770431,969706205,true +2018,2,20,1659347902,248851288,true +2018,2,20,380946293,1254365119,false +2018,2,20,699316059,2140392033,true +2018,2,20,237684630,909853053,false +2018,2,20,1367075942,790540619,true +2018,2,20,1274517869,1764489060,true +2018,2,20,132773456,1039872852,true +2018,2,20,1275500758,1559122137,true +2018,2,20,458123747,1107745658,false +2018,2,21,776010336,2128058552,true +2018,2,21,957033358,1732509981,true +2018,2,21,523839815,1299422387,false +2018,2,21,296591982,61350532,false +2018,2,21,1722489012,2034837702,false +2018,2,21,750506485,1944223297,false +2018,2,21,1000527354,1455101458,true +2018,2,21,198978897,1836604565,true +2018,2,21,201915350,1307957702,true +2018,2,21,66985678,854438002,false +2018,2,22,503998164,552983516,false +2018,2,22,895167211,404372399,true +2018,2,22,1027754342,319708882,true +2018,2,22,649927145,1878180986,false +2018,2,22,982223449,1038760111,false +2018,2,22,1791491047,1806973499,false +2018,2,22,2125972129,1401379560,false +2018,2,22,1578629882,867989090,false +2018,2,22,1492273082,795474437,false +2018,2,22,1515000761,1600496080,true +2018,2,23,1342637582,1711496992,false +2018,2,23,617203765,415351266,true +2018,2,23,562402081,2027936904,false +2018,2,23,257884762,1355978819,false +2018,2,23,1635672429,1800229934,true +2018,2,23,234713640,458057486,false +2018,2,23,954953787,931924402,true +2018,2,23,183767365,187457263,false +2018,2,23,1970782173,748383003,false +2018,2,23,44689624,846860094,false +2018,2,24,562384647,105308841,true +2018,2,24,215355313,1272131654,false +2018,2,24,501481548,1394078016,false +2018,2,24,259581472,706027070,true +2018,2,24,1445170911,1522308186,true +2018,2,24,1599810064,1072916377,false +2018,2,24,1104035877,879666052,false +2018,2,24,914254150,43809719,true +2018,2,24,192631749,1279996116,false +2018,2,24,1207691464,344973092,true +2018,2,25,1305011764,580541655,true +2018,2,25,1374035935,874111198,true +2018,2,25,1034033647,1161841127,true +2018,2,25,1277276721,663203934,true +2018,2,25,357333469,455621614,true +2018,2,25,1819752421,1188591196,true +2018,2,25,322352021,1868712189,false +2018,2,25,896166863,1183695487,true +2018,2,25,911381118,1121623105,true +2018,2,25,1671845654,1243074173,false +2018,2,26,1259314426,1773994040,true +2018,2,26,985339601,788321106,false +2018,2,26,203778850,289898050,false +2018,2,26,1109439376,1362200476,true +2018,2,26,1635963577,684732622,false +2018,2,26,1460962250,1496507965,true +2018,2,26,1052173927,1274755807,true +2018,2,26,445286826,593830606,false +2018,2,26,300127827,818021025,true +2018,2,26,728922543,434849166,true +2018,2,27,1628966577,756129338,true +2018,2,27,232155925,367516366,false +2018,2,27,1843842293,72788259,true +2018,2,27,827255825,237404680,true +2018,2,27,136076945,626836904,true +2018,2,27,1617044276,1261209385,true +2018,2,27,1098835086,1214886248,false +2018,2,27,2129665180,1221860526,false +2018,2,27,1108499824,1230134504,true +2018,2,27,304688313,165271466,false +2018,2,28,346843922,1404876173,true +2018,2,28,1219609551,786548431,true +2018,2,28,1711627393,1901401765,true +2018,2,28,1511781505,299070472,true +2018,2,28,939049992,56783176,true +2018,2,28,440311037,483419219,false +2018,2,28,477257701,348748846,false +2018,2,28,1749444017,1515506639,false +2018,2,28,840214619,102527952,true +2018,2,28,1265693061,1253522501,false +2018,2,29,687636512,855273588,false +2018,2,29,656898833,1503830669,false +2018,2,29,396647518,1911352048,true +2018,2,29,1703804750,1190265760,true +2018,2,29,79384508,1482883653,true +2018,2,29,1336857001,1233822782,false +2018,2,29,937412641,1747272551,true +2018,2,29,1376343686,1456909919,true +2018,2,29,2004357436,968911304,false +2018,2,29,1980004506,67538425,true +2018,2,30,731176435,312318595,true +2018,2,30,724732572,234631871,true +2018,2,30,332712259,328748345,false +2018,2,30,1825208895,1428338395,true +2018,2,30,915340088,566246654,false +2018,2,30,610325115,437496535,false +2018,2,30,1395292421,870623462,false +2018,2,30,573495940,184419636,false +2018,2,30,933637997,602910487,true +2018,2,30,429749256,693731420,false +2018,2,31,1646922991,1603794537,false +2018,2,31,1395132038,1050385393,true +2018,2,31,805672313,214235637,false +2018,2,31,699864798,779390638,true +2018,2,31,1664469907,1441361704,true +2018,2,31,2127884652,814533706,false +2018,2,31,2026908471,1123498552,true +2018,2,31,541940297,1014327078,true +2018,2,31,792678615,319981066,true +2018,2,31,58022533,393484205,false +2018,3,1,1030738753,1068280801,true +2018,3,1,453331585,449848228,false +2018,3,1,771252573,1251734651,false +2018,3,1,1493563556,608718314,true +2018,3,1,449473790,515944293,true +2018,3,1,217354536,1105163091,true +2018,3,1,1116938805,1640625827,false +2018,3,1,1216317112,357634976,true +2018,3,1,513093358,1584497676,false +2018,3,1,950697222,385863104,true +2018,3,2,392724674,476353250,false +2018,3,2,457633199,223632615,false +2018,3,2,1078374198,455848847,false +2018,3,2,861093123,1373501467,true +2018,3,2,685889258,742525072,false +2018,3,2,432539532,748070975,false +2018,3,2,754730599,1929931506,false +2018,3,2,629336678,1970030796,true +2018,3,2,993335170,2111750643,true +2018,3,2,1404811207,224789945,true +2018,3,3,1799862420,1229929631,false +2018,3,3,126041677,609637690,false +2018,3,3,522260217,1697604993,false +2018,3,3,1568494821,1110062692,true +2018,3,3,1770410131,908265412,false +2018,3,3,583536237,1828386264,false +2018,3,3,337740867,1157030487,false +2018,3,3,760389465,262303739,true +2018,3,3,458938771,515178735,true +2018,3,3,1739844875,2050018417,false +2018,3,4,1120486921,1535349754,false +2018,3,4,249872794,794005709,false +2018,3,4,1869095647,1344804839,true +2018,3,4,962037818,379697332,true +2018,3,4,1414688783,285989197,false +2018,3,4,866129285,208654316,true +2018,3,4,1471805937,1341798093,false +2018,3,4,1603511382,1050535683,true +2018,3,4,2101572837,1496562386,false +2018,3,4,1904454390,1425225114,true +2018,3,5,61261499,831526516,true +2018,3,5,1715391937,1646749049,true +2018,3,5,1552005074,1909619749,true +2018,3,5,2019085217,1502504944,true +2018,3,5,329004593,648258557,false +2018,3,5,1961172446,753101626,false +2018,3,5,378585854,1612199094,true +2018,3,5,345372726,360945617,true +2018,3,5,1014865691,823407543,false +2018,3,5,564768587,232177151,true +2018,3,6,1649370591,2046348231,false +2018,3,6,1754798773,605002255,false +2018,3,6,1799032090,355663159,false +2018,3,6,167681379,462873057,true +2018,3,6,1770145766,1602417574,false +2018,3,6,2107632143,1067153627,true +2018,3,6,1481907704,626891955,false +2018,3,6,277981939,1294597989,true +2018,3,6,270908266,1975975980,true +2018,3,6,1331739391,1342245235,false +2018,3,7,542252340,801871267,false +2018,3,7,405859388,980134857,false +2018,3,7,1511447764,107656309,false +2018,3,7,1546205735,1021792774,false +2018,3,7,1344866766,790827674,true +2018,3,7,1657540649,1119158159,false +2018,3,7,996559373,54289377,false +2018,3,7,705022830,803326955,false +2018,3,7,2099212880,183787106,false +2018,3,7,1901339858,335193927,true +2018,3,8,1825898021,1464402507,true +2018,3,8,737118873,1003474436,true +2018,3,8,2107200286,1608700503,false +2018,3,8,1213526833,1104403105,true +2018,3,8,936779077,107347849,false +2018,3,8,308947241,1412934509,true +2018,3,8,1563058943,1430599174,true +2018,3,8,1559776803,19490227,true +2018,3,8,876002519,1544096293,false +2018,3,8,639509877,71520873,false +2018,3,9,1357412843,1487326214,false +2018,3,9,167047330,1961111687,true +2018,3,9,1367274484,1777145178,false +2018,3,9,440669959,433747853,false +2018,3,9,190250519,1275955223,true +2018,3,9,1123534658,1880551656,true +2018,3,9,930988046,1235566161,false +2018,3,9,1431899195,978884910,true +2018,3,9,263226175,1669409401,true +2018,3,9,715112681,84865647,false +2018,3,10,276531678,1226534045,false +2018,3,10,30618902,466315017,true +2018,3,10,1829505076,1849834638,true +2018,3,10,711557843,1823744201,false +2018,3,10,2089113233,206686708,true +2018,3,10,195647333,2076203570,false +2018,3,10,1763671718,1773087411,false +2018,3,10,136944582,954777674,false +2018,3,10,476377391,97742128,true +2018,3,10,1001834519,1135511872,false +2018,3,11,309430578,205584219,false +2018,3,11,556864146,1030586552,false +2018,3,11,2087229515,374461574,true +2018,3,11,1553524188,121373878,true +2018,3,11,1491639215,2140160878,false +2018,3,11,993094604,795792977,false +2018,3,11,1599536113,1782188806,true +2018,3,11,785535230,1648117684,false +2018,3,11,1007544758,104245217,false +2018,3,11,631931595,579650846,false +2018,3,12,17995725,1691718995,false +2018,3,12,420174975,2005895391,true +2018,3,12,1448005488,249535639,false +2018,3,12,2002637283,1697024636,false +2018,3,12,1791079832,1259372963,true +2018,3,12,510555252,2134964109,true +2018,3,12,910663844,909600887,false +2018,3,12,1764754913,1530384616,false +2018,3,12,940483529,2139043578,true +2018,3,12,497186210,228864310,true +2018,3,13,686754305,1860032206,false +2018,3,13,1716189383,291854859,true +2018,3,13,444460153,754704291,false +2018,3,13,268710487,1862302487,true +2018,3,13,328298664,643149287,false +2018,3,13,930574282,2134067975,true +2018,3,13,758028438,1080855279,false +2018,3,13,263954571,265456680,true +2018,3,13,1898970110,2035884777,true +2018,3,13,1757886601,570918656,true +2018,3,14,81447756,876110386,true +2018,3,14,1835035959,823391041,false +2018,3,14,1313289648,1313261297,false +2018,3,14,2080735631,13550573,false +2018,3,14,1574781361,1944292603,true +2018,3,14,1750397172,1720280456,true +2018,3,14,2122148871,1656858992,true +2018,3,14,1396476427,739984800,false +2018,3,14,1889174192,594976915,false +2018,3,14,17678808,722960670,true +2018,3,15,864109166,660777926,false +2018,3,15,1144325326,1184918292,true +2018,3,15,741526328,1476376249,true +2018,3,15,1734827662,1489489931,true +2018,3,15,1907694658,1253589536,true +2018,3,15,459590278,273054200,true +2018,3,15,1276379813,1675659164,false +2018,3,15,1110617781,2008946534,false +2018,3,15,677662169,1896684943,false +2018,3,15,965719865,1089885305,true +2018,3,16,656491375,905847576,true +2018,3,16,1032082554,488652489,false +2018,3,16,1718304725,566590314,false +2018,3,16,111595269,1374487892,true +2018,3,16,2062533650,2105812277,false +2018,3,16,877792121,1092705268,true +2018,3,16,1672736939,47584742,true +2018,3,16,1702299159,723734043,false +2018,3,16,1666133053,939950558,false +2018,3,16,1690603729,1580904128,false +2018,3,17,1911324783,539423220,true +2018,3,17,968509417,945660871,false +2018,3,17,1110811198,211960799,false +2018,3,17,1721487494,1935907144,true +2018,3,17,363705058,81757786,true +2018,3,17,475699648,1292715391,true +2018,3,17,2121669806,1066326817,true +2018,3,17,1163285531,1410064466,false +2018,3,17,857969598,551614485,true +2018,3,17,326136934,1099651496,false +2018,3,18,614581337,496948777,true +2018,3,18,1572613744,49922052,false +2018,3,18,812820801,1515612142,true +2018,3,18,872067364,228801837,true +2018,3,18,1566274160,1505239758,true +2018,3,18,1564008665,758620357,true +2018,3,18,1925734648,1132629339,true +2018,3,18,2109711874,907637008,true +2018,3,18,594974450,1855009339,false +2018,3,18,1784083840,137590896,false +2018,3,19,327525069,1963793048,false +2018,3,19,628378947,2065748530,false +2018,3,19,1119425368,4299813,true +2018,3,19,905136894,911483840,false +2018,3,19,1870575188,1045014022,true +2018,3,19,1500484635,1321207647,false +2018,3,19,306304384,1655840077,false +2018,3,19,496848180,265765880,true +2018,3,19,1159980627,746919108,true +2018,3,19,1897213107,440750494,false +2018,3,20,61619933,519114041,false +2018,3,20,2119473007,1996778706,true +2018,3,20,1707773584,1234551436,true +2018,3,20,1510244192,1397697207,true +2018,3,20,2118820828,2032510766,true +2018,3,20,883669948,921218731,true +2018,3,20,612270047,234218192,true +2018,3,20,120952607,1373920773,false +2018,3,20,1551921049,1452130930,true +2018,3,20,562768776,1595918573,false +2018,3,21,805299819,81188630,false +2018,3,21,281486676,1809221502,true +2018,3,21,345946052,1440667072,true +2018,3,21,1922989437,1978568892,false +2018,3,21,404364868,1847555728,true +2018,3,21,513145028,611722496,false +2018,3,21,1055673549,801923596,true +2018,3,21,807505049,408704997,false +2018,3,21,2123637290,610697983,true +2018,3,21,117933009,79144415,false +2018,3,22,1945408087,1620779129,true +2018,3,22,2022321295,1716477955,false +2018,3,22,735805593,1358284490,false +2018,3,22,1040076276,495789228,false +2018,3,22,1628875740,1640599447,false +2018,3,22,1655598947,590521843,false +2018,3,22,628556566,519673946,true +2018,3,22,553151616,648591175,false +2018,3,22,1910032797,724810722,false +2018,3,22,969324609,1251578235,false +2018,3,23,1518760888,2121514680,false +2018,3,23,1411210902,1700233512,true +2018,3,23,1526309325,2029084535,true +2018,3,23,1766201246,267408694,false +2018,3,23,1331244282,1889006442,false +2018,3,23,331475044,2079192041,true +2018,3,23,215769598,901952793,true +2018,3,23,842706066,953027268,true +2018,3,23,1588944831,1461943691,false +2018,3,23,98945442,1351739928,false +2018,3,24,1039770828,208771684,true +2018,3,24,502990708,1731188527,true +2018,3,24,335952807,1615372841,true +2018,3,24,44591951,1817151548,true +2018,3,24,263862216,1146740048,true +2018,3,24,60082615,1946330016,false +2018,3,24,1603233169,774497764,true +2018,3,24,1625743527,2140278822,true +2018,3,24,1220984232,1234489577,true +2018,3,24,201064197,791361091,true +2018,3,25,1933076593,81812421,true +2018,3,25,1483134825,1100092026,false +2018,3,25,1269397676,1330218317,false +2018,3,25,362933146,1223866231,false +2018,3,25,499321941,434339163,true +2018,3,25,1549751111,860793470,false +2018,3,25,668846420,1839480551,false +2018,3,25,270355427,1839198011,true +2018,3,25,294371250,211830812,false +2018,3,25,723079342,645378082,true +2018,3,26,1246965056,607455467,false +2018,3,26,1160593751,525584085,false +2018,3,26,1837858547,1331731451,true +2018,3,26,151497400,1804274768,true +2018,3,26,423610983,1345224465,false +2018,3,26,584804501,272227013,false +2018,3,26,1232528964,177411909,false +2018,3,26,1784078446,177183605,false +2018,3,26,1780535932,326314646,true +2018,3,26,337171647,1745103081,true +2018,3,27,1254513052,944696258,true +2018,3,27,2094175554,629951056,false +2018,3,27,1211452062,1624313456,true +2018,3,27,522513430,1203447538,true +2018,3,27,779054857,1138125065,false +2018,3,27,1079503053,89807098,false +2018,3,27,31064895,41434833,false +2018,3,27,748446167,510250247,false +2018,3,27,744490816,102643747,true +2018,3,27,1830879686,788394039,false +2018,3,28,1278463063,1961009118,true +2018,3,28,343525717,475488439,true +2018,3,28,77578341,641631331,false +2018,3,28,1883727939,998798588,true +2018,3,28,1791439306,1900554161,false +2018,3,28,1624800855,966200356,false +2018,3,28,1822207768,275680645,true +2018,3,28,120064819,1961641537,false +2018,3,28,1681607702,1922842509,true +2018,3,28,1714282165,1408732526,true +2018,3,29,1809091008,956456710,true +2018,3,29,354225435,156881160,false +2018,3,29,718238017,1848516705,false +2018,3,29,522981678,671844959,true +2018,3,29,604221980,1343596739,false +2018,3,29,1803990032,49948334,true +2018,3,29,1312936450,1352615551,true +2018,3,29,618452364,761091157,true +2018,3,29,277334279,2002646230,false +2018,3,29,1973295350,1298366834,true +2018,3,30,1783058223,406345886,true +2018,3,30,452598267,1193748596,false +2018,3,30,888387723,1128904365,true +2018,3,30,1061777911,188244804,false +2018,3,30,2021856103,32906766,true +2018,3,30,844976550,1968912396,false +2018,3,30,573530154,475590507,true +2018,3,30,727913543,524434128,false +2018,3,30,1801895168,775249978,false +2018,3,30,684574867,900007240,false +2018,3,31,2116374377,2059363204,false +2018,3,31,165251350,1262795466,false +2018,3,31,1086989363,1072550755,false +2018,3,31,1539403969,362274841,false +2018,3,31,305478301,1981783443,true +2018,3,31,469782998,100618107,false +2018,3,31,1536952711,1026646683,false +2018,3,31,67822720,1723109219,false +2018,3,31,983700986,2060008063,false +2018,3,31,345388975,2006343308,true +2018,4,1,67844219,879131393,false +2018,4,1,674404881,1855091302,false +2018,4,1,993985241,1587296975,false +2018,4,1,1018118720,1647197458,true +2018,4,1,288483960,1320235580,false +2018,4,1,1670939774,2038867567,false +2018,4,1,2113305418,2126927178,false +2018,4,1,907391383,35522524,false +2018,4,1,1726182902,1350067257,false +2018,4,1,649502140,417201343,false +2018,4,2,78342107,1983809235,true +2018,4,2,1242994547,940100089,false +2018,4,2,436114925,2083071017,true +2018,4,2,1781798399,258264252,false +2018,4,2,2074348455,141450116,true +2018,4,2,1615588281,362782092,false +2018,4,2,2111100287,478944437,true +2018,4,2,137110620,197783127,false +2018,4,2,1651176097,891268361,false +2018,4,2,118437941,1443869396,true +2018,4,3,1779559395,174963588,false +2018,4,3,509260966,682125501,false +2018,4,3,1629113891,2009649727,false +2018,4,3,1846884242,2070636919,false +2018,4,3,1381242005,138626032,false +2018,4,3,1309437570,569221442,false +2018,4,3,899312562,1441823757,false +2018,4,3,914621340,2096490511,false +2018,4,3,2073604028,622601477,false +2018,4,3,754373069,1999115914,false +2018,4,4,2042376724,870169474,false +2018,4,4,165850804,1432149496,true +2018,4,4,805222108,835727034,false +2018,4,4,1237345327,105706490,false +2018,4,4,243585173,90172720,false +2018,4,4,1573545108,769416024,true +2018,4,4,1265291709,2134409843,true +2018,4,4,1228942336,317786462,false +2018,4,4,481823894,795706824,true +2018,4,4,478572102,574942063,false +2018,4,5,730797979,1167743671,true +2018,4,5,328660060,1057747676,false +2018,4,5,619895127,535297014,true +2018,4,5,1846654303,472145185,false +2018,4,5,1341835407,58721838,true +2018,4,5,58463873,341998873,true +2018,4,5,79177222,1346259840,false +2018,4,5,6136448,1730536898,true +2018,4,5,1935969086,2018357727,false +2018,4,5,61523057,1290244521,false +2018,4,6,133041671,523160986,false +2018,4,6,1019704078,1885243937,true +2018,4,6,1385953984,1546444455,true +2018,4,6,1871450309,1157707709,true +2018,4,6,699520902,1925997196,false +2018,4,6,978739761,333904343,true +2018,4,6,800428243,917675517,false +2018,4,6,306524111,820606216,true +2018,4,6,173894715,661268615,false +2018,4,6,873349387,303843537,false +2018,4,7,1968584769,1865019877,false +2018,4,7,1803070964,1587092210,true +2018,4,7,89733246,1869364165,false +2018,4,7,932141626,1315255068,true +2018,4,7,113382287,2012015666,false +2018,4,7,1485608776,470600705,true +2018,4,7,1310825611,1819491295,true +2018,4,7,1571142801,246011633,true +2018,4,7,700204008,2029283584,false +2018,4,7,1164364907,780322180,false +2018,4,8,1392466967,690721395,false +2018,4,8,59722003,990384386,false +2018,4,8,1067170889,671055985,false +2018,4,8,898367652,382351746,false +2018,4,8,60209171,1702409975,true +2018,4,8,1541480496,1794565891,false +2018,4,8,1870359790,900271015,true +2018,4,8,1738874172,1763919074,true +2018,4,8,1541480128,1241508824,false +2018,4,8,408046147,507489403,true +2018,4,9,2086294268,1639110914,false +2018,4,9,1591871004,281071068,false +2018,4,9,2141298164,2105356503,false +2018,4,9,227194700,1060359629,false +2018,4,9,1536833647,83585947,true +2018,4,9,281017258,133944992,false +2018,4,9,959218603,1662019236,true +2018,4,9,1319825898,1318219138,false +2018,4,9,449982535,675045889,true +2018,4,9,1604654561,49418309,true +2018,4,10,783210705,504356796,false +2018,4,10,2060591516,1659453401,true +2018,4,10,731650799,722788403,true +2018,4,10,1959367855,853522932,false +2018,4,10,625123919,858315392,true +2018,4,10,313866297,1663062161,false +2018,4,10,2032320238,38050015,true +2018,4,10,994771348,558307413,false +2018,4,10,659100756,418226327,false +2018,4,10,1118004909,814784246,true +2018,4,11,33571281,1033458137,false +2018,4,11,1950637688,1931874618,false +2018,4,11,2113676262,1487161221,true +2018,4,11,1431909622,1455537195,false +2018,4,11,565382488,1093271518,true +2018,4,11,362929612,39106443,true +2018,4,11,858596009,343428749,false +2018,4,11,285530463,2146621367,false +2018,4,11,1540451552,1901057418,false +2018,4,11,1549656037,1173214065,false +2018,4,12,2046185710,794596804,true +2018,4,12,1092332283,2082989650,false +2018,4,12,1081241361,1621025175,false +2018,4,12,1680355734,647279303,true +2018,4,12,302092799,909273718,true +2018,4,12,811191141,1725157104,true +2018,4,12,1497159625,1512904792,false +2018,4,12,1667745804,281660862,false +2018,4,12,1261350570,242066622,true +2018,4,12,2109367222,463784463,false +2018,4,13,963849800,1140843638,false +2018,4,13,1213363694,1653214159,true +2018,4,13,2098713100,1437405793,true +2018,4,13,145232071,397434686,false +2018,4,13,359019420,1153276649,true +2018,4,13,1270699445,40547558,false +2018,4,13,2037591200,81373931,true +2018,4,13,1263688361,1736486127,false +2018,4,13,1194675536,671182394,false +2018,4,13,1166796105,1636195021,true +2018,4,14,266135944,160844045,true +2018,4,14,1497256802,1452598911,true +2018,4,14,65974352,489536759,true +2018,4,14,1182994142,119359190,true +2018,4,14,661447194,391963958,true +2018,4,14,1930279145,537080962,false +2018,4,14,758601760,1754388343,true +2018,4,14,285252365,1090926541,true +2018,4,14,752860078,1607830896,false +2018,4,14,710489700,1951556436,true +2018,4,15,484775487,1567463632,true +2018,4,15,1458489398,791837725,true +2018,4,15,894131111,1560188540,true +2018,4,15,2023202976,1150684125,false +2018,4,15,1235437013,1007578902,false +2018,4,15,1014956821,651867202,false +2018,4,15,1133698497,149209649,false +2018,4,15,1571113426,278405796,false +2018,4,15,169144228,959360726,true +2018,4,15,369491107,866634095,true +2018,4,16,1280735386,724391699,true +2018,4,16,483641485,195500311,false +2018,4,16,657287725,1042894290,true +2018,4,16,2029951280,781575296,false +2018,4,16,2133851267,768828720,false +2018,4,16,1706806788,196607257,true +2018,4,16,183157282,867787006,true +2018,4,16,1308652735,732262690,true +2018,4,16,1324144776,1299493951,true +2018,4,16,530048897,254821069,false +2018,4,17,511407884,371063226,false +2018,4,17,546961569,485521729,true +2018,4,17,1667420606,262280955,true +2018,4,17,100157222,286991522,true +2018,4,17,1583568958,2027437650,false +2018,4,17,1941782101,61400242,false +2018,4,17,1890900172,135929788,true +2018,4,17,1130688482,671794592,true +2018,4,17,480873977,756210100,false +2018,4,17,587873182,1914595618,true +2018,4,18,799123901,1843688024,true +2018,4,18,222617783,2074968562,true +2018,4,18,884742881,928456305,true +2018,4,18,200799206,1567700543,false +2018,4,18,1527128581,1613705017,true +2018,4,18,1667111722,1450449332,false +2018,4,18,1120100768,1254455542,false +2018,4,18,1391569619,910731813,false +2018,4,18,240744657,165098432,false +2018,4,18,1203478795,96838142,true +2018,4,19,1960475353,1490291980,true +2018,4,19,153900072,771987979,false +2018,4,19,970048916,1457990617,false +2018,4,19,1895548263,1382705201,true +2018,4,19,1212639056,132094605,true +2018,4,19,1837350111,899808046,false +2018,4,19,424902863,1784197343,false +2018,4,19,1681550539,1045377921,true +2018,4,19,60844631,1205863785,false +2018,4,19,643180932,895350782,false +2018,4,20,1569647392,1899884979,true +2018,4,20,1815615675,1925615812,false +2018,4,20,1493833852,1452481753,true +2018,4,20,156788179,1405926793,false +2018,4,20,419071630,1282514968,true +2018,4,20,1145559665,490607738,true +2018,4,20,899933809,44924386,false +2018,4,20,838566293,1236110629,true +2018,4,20,1190268436,138288977,true +2018,4,20,1580435675,104447773,true +2018,4,21,219104316,1801104653,false +2018,4,21,1685632696,679992929,false +2018,4,21,409311084,1801203709,true +2018,4,21,501667279,1234169770,false +2018,4,21,637505696,2110440293,false +2018,4,21,607073454,1713347885,true +2018,4,21,1903479581,2037683507,true +2018,4,21,193928849,2106888304,false +2018,4,21,388985035,285035185,true +2018,4,21,1290980616,970510408,true +2018,4,22,1951293128,37187289,false +2018,4,22,3609480,862087705,false +2018,4,22,1462917185,1470506227,false +2018,4,22,964279634,2074227690,false +2018,4,22,167001519,1650410826,true +2018,4,22,2029687612,1660799222,true +2018,4,22,1570721032,1815255841,true +2018,4,22,444797489,1646826675,true +2018,4,22,1868106631,416612905,false +2018,4,22,754996371,216260751,true +2018,4,23,1162097685,316272419,true +2018,4,23,601251772,673904340,false +2018,4,23,1860955519,484790911,false +2018,4,23,2041855053,234789227,true +2018,4,23,1571762679,1686506765,true +2018,4,23,1663093528,1124717949,true +2018,4,23,307479953,1414475292,false +2018,4,23,1384544701,1831122205,false +2018,4,23,1789258691,2055092126,true +2018,4,23,842178903,928646790,false +2018,4,24,1611877385,908492844,true +2018,4,24,1425589962,1351251088,false +2018,4,24,39367743,1364642809,true +2018,4,24,1065088914,262016609,false +2018,4,24,861123411,592784664,false +2018,4,24,38037285,994452,true +2018,4,24,729936210,2029942947,false +2018,4,24,371068996,630124608,true +2018,4,24,593740297,338686368,true +2018,4,24,480297677,2103997609,true +2018,4,25,1936737708,722633661,false +2018,4,25,398098922,2077214339,true +2018,4,25,1792380733,895211318,false +2018,4,25,1248573376,386653194,true +2018,4,25,628378290,120650883,false +2018,4,25,781736666,1288023908,false +2018,4,25,499993860,1919170974,false +2018,4,25,342490883,614748414,false +2018,4,25,1502668461,1510536197,false +2018,4,25,201506400,936892902,true +2018,4,26,38260036,991992259,false +2018,4,26,424100053,907585115,true +2018,4,26,64981811,1856903460,false +2018,4,26,1982576415,1758576379,true +2018,4,26,1847569598,1201977820,false +2018,4,26,111850544,921494363,false +2018,4,26,555214805,1299600336,false +2018,4,26,530660006,298046197,true +2018,4,26,61100127,1982643904,false +2018,4,26,182347136,1498958244,true +2018,4,27,861199626,1738881920,true +2018,4,27,1640077906,1053637423,false +2018,4,27,15605955,210391686,true +2018,4,27,409764007,1829125522,true +2018,4,27,1824963420,1319273714,false +2018,4,27,1490541924,552817660,false +2018,4,27,100642827,474416225,false +2018,4,27,1920416306,660634823,true +2018,4,27,549788369,531481380,true +2018,4,27,618643555,1775375056,true +2018,4,28,1416869345,859529658,false +2018,4,28,472724437,1421168374,false +2018,4,28,652269212,87234451,false +2018,4,28,178542003,1103270062,true +2018,4,28,523505097,184648847,false +2018,4,28,1523939373,348953836,true +2018,4,28,460388613,1951509940,false +2018,4,28,1866171254,491997394,false +2018,4,28,1002316714,1974276875,false +2018,4,28,130247093,1087052256,true +2018,4,29,1977328535,1527651760,false +2018,4,29,2030820004,567793567,false +2018,4,29,118323869,919727207,true +2018,4,29,1498693897,635103996,true +2018,4,29,1926119305,558895716,true +2018,4,29,1322353510,330057163,true +2018,4,29,1621441013,1784545025,true +2018,4,29,254080603,238385452,true +2018,4,29,1207337582,1600900760,true +2018,4,29,2075802296,801778633,true +2018,4,30,1956506767,677512123,false +2018,4,30,1765323316,2065324326,true +2018,4,30,64492320,1339387262,false +2018,4,30,1894751550,1572616567,false +2018,4,30,206888540,316152470,true +2018,4,30,2064458092,1010529722,false +2018,4,30,793828073,1911880302,false +2018,4,30,771898214,1796664841,false +2018,4,30,312394796,973505678,false +2018,4,30,366692459,779020380,true +2018,4,31,461393652,1495776224,false +2018,4,31,64959168,1587776565,true +2018,4,31,1584372157,1384642232,false +2018,4,31,532722082,978004975,false +2018,4,31,1716796916,1611419051,true +2018,4,31,512575643,1402771105,true +2018,4,31,335929860,512377498,true +2018,4,31,59118405,905090517,false +2018,4,31,1580723731,746874493,true +2018,4,31,2048770528,1828282506,true +2018,5,1,319796666,1874284491,false +2018,5,1,1567475786,167723316,true +2018,5,1,176723742,933258395,true +2018,5,1,1279816755,303070902,true +2018,5,1,1983138859,163291852,false +2018,5,1,1747302567,175691914,true +2018,5,1,1187284206,489553370,false +2018,5,1,1733413960,1494164636,false +2018,5,1,1210903099,1807281446,true +2018,5,1,1357221064,1749150068,false +2018,5,2,1781034522,1680125661,true +2018,5,2,291292547,1696971217,true +2018,5,2,1516528570,443179717,false +2018,5,2,1043023951,800763580,false +2018,5,2,1222167259,1793574289,true +2018,5,2,510267092,1981297590,true +2018,5,2,1529649897,580112532,true +2018,5,2,1353558032,481524897,true +2018,5,2,655046720,865018598,false +2018,5,2,1749080273,1767571296,true +2018,5,3,339585613,1690546265,false +2018,5,3,1832705867,1953577834,false +2018,5,3,908473235,475860943,true +2018,5,3,529860885,978028488,true +2018,5,3,2007147700,1035711955,false +2018,5,3,2141687852,1387961390,true +2018,5,3,1874573834,578339197,true +2018,5,3,976342744,1940235316,true +2018,5,3,397678469,859476241,true +2018,5,3,1301486465,1357181917,false +2018,5,4,786274483,1077045464,false +2018,5,4,1140774096,408255205,true +2018,5,4,1216920396,413458966,true +2018,5,4,600548282,2078533779,false +2018,5,4,634419354,1203693886,true +2018,5,4,564235335,1903141891,false +2018,5,4,1419312773,1131059627,false +2018,5,4,1277255463,850877727,false +2018,5,4,685044540,1589547647,false +2018,5,4,1820544381,1521457349,false +2018,5,5,1836209833,1094796164,true +2018,5,5,213259629,796346062,false +2018,5,5,1994144561,1766029849,false +2018,5,5,1047021889,312392421,true +2018,5,5,1250132855,1805841815,false +2018,5,5,250012746,1156405629,true +2018,5,5,1925719317,1403604913,true +2018,5,5,1203931083,129269828,true +2018,5,5,1568878725,1102373048,true +2018,5,5,721659786,1198044638,false +2018,5,6,864853175,749324498,false +2018,5,6,1690792156,1822068441,false +2018,5,6,871801095,889699961,false +2018,5,6,1241948233,1535116810,false +2018,5,6,191550086,1810759657,false +2018,5,6,189110553,730191071,false +2018,5,6,1110331870,1540331585,false +2018,5,6,112998251,520795209,false +2018,5,6,248294712,2016305792,true +2018,5,6,327930020,1200089407,true +2018,5,7,334151140,225944492,true +2018,5,7,2139354968,435824669,true +2018,5,7,776031164,33557179,false +2018,5,7,983674620,1394229308,false +2018,5,7,201568375,1377447970,false +2018,5,7,139008253,1411921301,true +2018,5,7,1694606179,1895276561,true +2018,5,7,954013719,1630470238,false +2018,5,7,891343428,1134813627,true +2018,5,7,616710144,887630544,false +2018,5,8,443975242,1729043276,true +2018,5,8,1321694910,168395680,true +2018,5,8,336747606,1166325074,false +2018,5,8,78987843,1064052688,true +2018,5,8,968334859,360516034,false +2018,5,8,1619568681,591581405,true +2018,5,8,1189420383,118000104,false +2018,5,8,572855964,1310764162,false +2018,5,8,1378166437,900279253,false +2018,5,8,1055708379,1257019769,false +2018,5,9,77937990,302263099,true +2018,5,9,859129555,87853322,true +2018,5,9,1079276968,1004767913,false +2018,5,9,1909830335,352290636,true +2018,5,9,925330161,615717287,true +2018,5,9,1383039196,1680579668,false +2018,5,9,1657379237,792224038,true +2018,5,9,331279492,141027028,false +2018,5,9,73424980,1873644348,false +2018,5,9,523576150,611257560,false +2018,5,10,44473813,1030517806,true +2018,5,10,640723510,844495259,true +2018,5,10,906946746,822436385,true +2018,5,10,281010500,491644845,false +2018,5,10,835927560,52082207,false +2018,5,10,1288337182,1127578853,true +2018,5,10,923063636,1897665577,true +2018,5,10,1332336907,1149063633,true +2018,5,10,241571537,1364545931,true +2018,5,10,1564213468,1293562015,false +2018,5,11,1936690176,1038270388,false +2018,5,11,88521388,1593817016,true +2018,5,11,1250598384,1397847863,true +2018,5,11,2001234882,130378372,false +2018,5,11,392240219,1127279048,false +2018,5,11,933550402,958310163,false +2018,5,11,16609024,180783954,false +2018,5,11,1712601203,787153298,true +2018,5,11,583468809,603300208,true +2018,5,11,974537663,584269538,true +2018,5,12,1153611874,2013695479,false +2018,5,12,1690105533,743054765,true +2018,5,12,1983128388,1434059860,false +2018,5,12,543637731,1030578621,true +2018,5,12,1328600170,380558053,false +2018,5,12,1087717811,1556658065,true +2018,5,12,648498852,1157324954,false +2018,5,12,313051773,1376917087,true +2018,5,12,235625160,1751489661,true +2018,5,12,1997979919,1044829437,false +2018,5,13,168350510,1305374716,false +2018,5,13,2087904230,1291698804,true +2018,5,13,1148794425,933858813,false +2018,5,13,1051667399,529583390,false +2018,5,13,1890889631,71591978,true +2018,5,13,167503803,563555236,false +2018,5,13,693799534,1283909884,true +2018,5,13,2000455661,1022763322,true +2018,5,13,1951411706,1559092322,true +2018,5,13,701966555,1032612576,false +2018,5,14,1814656572,390961702,false +2018,5,14,1108878285,1322562974,false +2018,5,14,664630352,30846030,true +2018,5,14,475955873,1873493484,false +2018,5,14,1533214214,292165944,true +2018,5,14,1647772737,69105354,false +2018,5,14,1883241423,892435487,true +2018,5,14,1496822882,1344955182,false +2018,5,14,1689496169,1434681407,true +2018,5,14,870958168,1304540890,true +2018,5,15,11606542,1809871335,false +2018,5,15,2068816937,99216310,false +2018,5,15,873309990,1698610701,false +2018,5,15,140845608,690671416,true +2018,5,15,138152088,127677499,false +2018,5,15,1263199410,1561112462,false +2018,5,15,61107796,1203765244,false +2018,5,15,225948911,873452160,false +2018,5,15,1862080183,2036950538,true +2018,5,15,453778084,1500860483,false +2018,5,16,2009877447,1199091431,true +2018,5,16,1097369713,43866804,true +2018,5,16,785379715,114826168,true +2018,5,16,1104026900,23426484,true +2018,5,16,791299259,457284454,false +2018,5,16,451778958,1059962786,true +2018,5,16,1444228650,1483736446,false +2018,5,16,1386802487,1946038555,false +2018,5,16,364588998,1433285961,false +2018,5,16,1454927906,540164498,true +2018,5,17,1887404674,49236742,false +2018,5,17,1718477158,1391243865,false +2018,5,17,1748961533,112126552,true +2018,5,17,1417826850,1646261299,false +2018,5,17,1391286221,148984898,false +2018,5,17,685285208,409385863,true +2018,5,17,432524900,830343241,false +2018,5,17,503892980,541619656,true +2018,5,17,603169739,1003355022,false +2018,5,17,123715995,285114042,false +2018,5,18,784953500,1756610050,true +2018,5,18,1615759887,482918031,false +2018,5,18,1768633350,1159259398,true +2018,5,18,12141651,1980092950,false +2018,5,18,1683137480,1341855760,false +2018,5,18,200523732,224826482,true +2018,5,18,105502554,198965033,true +2018,5,18,1588335927,393537046,false +2018,5,18,793550016,444273767,false +2018,5,18,2043975595,764374770,false +2018,5,19,2039812547,936849583,true +2018,5,19,726229705,1439754778,true +2018,5,19,1661077114,442247516,false +2018,5,19,1318283362,1156699984,true +2018,5,19,294405740,121329576,true +2018,5,19,644187862,2074299185,false +2018,5,19,133938101,1579408353,false +2018,5,19,528985027,849121533,false +2018,5,19,1266975732,1457274326,false +2018,5,19,1801562158,485652777,true +2018,5,20,280760552,913129728,true +2018,5,20,1056567924,1449989700,true +2018,5,20,328752899,528127564,true +2018,5,20,21786532,941186517,true +2018,5,20,1766308458,713983642,true +2018,5,20,31478035,1780561207,true +2018,5,20,1166182158,1217697863,true +2018,5,20,995191880,1217589919,false +2018,5,20,620600035,1417731936,false +2018,5,20,800457362,1356191959,false +2018,5,21,469369191,1187507021,false +2018,5,21,1016857316,538157134,true +2018,5,21,1681720818,656502290,true +2018,5,21,1120680912,1935624839,true +2018,5,21,2079536776,1116117124,false +2018,5,21,477889717,651842841,false +2018,5,21,352600864,887032824,false +2018,5,21,1305570425,301299635,false +2018,5,21,1065434529,1895038293,false +2018,5,21,620710251,2143255788,true +2018,5,22,1598512389,1935804062,false +2018,5,22,528967968,1013516221,false +2018,5,22,1056379488,1299053378,false +2018,5,22,1788143716,158353401,false +2018,5,22,1535215801,435171540,false +2018,5,22,1895316123,1639730729,true +2018,5,22,1889050880,735174916,false +2018,5,22,367783204,1387850627,false +2018,5,22,1748087166,1468064485,false +2018,5,22,573345438,718103817,false +2018,5,23,367514773,1395902697,false +2018,5,23,358130456,2113980647,false +2018,5,23,1707930796,434226711,false +2018,5,23,1454769563,1912404906,true +2018,5,23,1134695318,1539467372,false +2018,5,23,266392717,1449633516,true +2018,5,23,1886919133,875988836,false +2018,5,23,1938479577,128427392,false +2018,5,23,1024906924,1244695830,true +2018,5,23,1155569899,1099655500,true +2018,5,24,130765657,935513463,false +2018,5,24,1542953939,1092465999,false +2018,5,24,1725973946,1954071188,true +2018,5,24,2135330358,1914827395,true +2018,5,24,1866584772,2039418345,true +2018,5,24,1494155220,1237840028,false +2018,5,24,913508141,946265626,true +2018,5,24,1173330375,595794903,true +2018,5,24,1912667654,1498815932,false +2018,5,24,1463744720,1553311572,false +2018,5,25,1571990918,1229351671,true +2018,5,25,1928301639,2067460009,false +2018,5,25,1788432507,360880409,true +2018,5,25,1439538309,48954298,true +2018,5,25,1375498021,681849978,true +2018,5,25,752174316,1711575547,true +2018,5,25,1468347485,337149511,false +2018,5,25,1027050707,374105750,true +2018,5,25,791286872,1266444549,true +2018,5,25,1316368376,1063420737,true +2018,5,26,129523822,746100716,true +2018,5,26,1224532568,2000077724,false +2018,5,26,1059800996,45486703,false +2018,5,26,1724267631,1487481197,true +2018,5,26,1003259370,1765315207,false +2018,5,26,949539919,1848734235,true +2018,5,26,809219155,1085267871,false +2018,5,26,167159064,2080164661,false +2018,5,26,2021546668,118741293,false +2018,5,26,1064016299,1081234919,true +2018,5,27,1018208062,119634789,true +2018,5,27,1925654711,1941727655,false +2018,5,27,1115704223,1568576900,false +2018,5,27,1434795888,933348489,false +2018,5,27,1645798876,636480668,true +2018,5,27,1306693001,2102862833,true +2018,5,27,1785095016,1910861565,false +2018,5,27,12808274,1353624229,true +2018,5,27,1479970700,1980414847,true +2018,5,27,881363968,433721706,true +2018,5,28,1440297298,1734468950,true +2018,5,28,1339255184,1618048427,false +2018,5,28,91315488,406509640,true +2018,5,28,787250369,216055268,false +2018,5,28,26459258,1424253519,false +2018,5,28,748984594,844533186,true +2018,5,28,1678115498,674987791,false +2018,5,28,1154573787,1580905526,false +2018,5,28,171102012,1925865132,true +2018,5,28,2062524610,900688881,true +2018,5,29,61208459,901813474,true +2018,5,29,2068398920,432849668,false +2018,5,29,1758078685,164116076,false +2018,5,29,2129181480,98277520,false +2018,5,29,1421347001,410335776,false +2018,5,29,1280747178,1043654610,true +2018,5,29,1338921850,926648392,true +2018,5,29,423869769,276372975,false +2018,5,29,657594705,2126548507,true +2018,5,29,1875184191,600050761,true +2018,5,30,359158833,1906588783,true +2018,5,30,228274626,2004237125,true +2018,5,30,492354909,176021486,true +2018,5,30,1461547813,616104392,true +2018,5,30,1174027150,1382518399,true +2018,5,30,1347635488,2071225307,false +2018,5,30,1288805095,1209059484,false +2018,5,30,1214922453,1670145839,false +2018,5,30,213542968,2070001351,false +2018,5,30,108305282,967759481,false +2018,5,31,1554103614,613739115,true +2018,5,31,667301850,1322083559,true +2018,5,31,1561664988,107482812,true +2018,5,31,905574258,533027089,true +2018,5,31,1059669640,1419530623,false +2018,5,31,1975012817,2043374390,true +2018,5,31,1044023898,905366714,true +2018,5,31,484948480,2074469547,true +2018,5,31,1110584201,1866379706,false +2018,5,31,1667140775,1079074498,true +2018,6,1,1730249519,538620668,true +2018,6,1,981101600,513166737,true +2018,6,1,1324869042,644283859,true +2018,6,1,1286090510,273342307,true +2018,6,1,1687521059,712527326,true +2018,6,1,2098045254,1414607804,true +2018,6,1,2129282925,1184109493,false +2018,6,1,2006444096,1754043694,true +2018,6,1,1088475188,1734319442,false +2018,6,1,1917539136,1787283326,false +2018,6,2,1914035880,182745626,true +2018,6,2,1291632888,1449439167,true +2018,6,2,1435075010,325338528,false +2018,6,2,855586644,908340848,true +2018,6,2,1473286561,1468296041,true +2018,6,2,1585521581,1582367686,true +2018,6,2,1605058300,272081762,true +2018,6,2,2043725979,983471263,true +2018,6,2,1137271280,1370532301,false +2018,6,2,379384850,360477397,false +2018,6,3,922006923,398679743,true +2018,6,3,157942684,819541490,true +2018,6,3,254181850,1253562518,true +2018,6,3,1545653707,1001752937,false +2018,6,3,1249568893,1895848423,false +2018,6,3,1550847788,1317399519,true +2018,6,3,691349461,2146916052,true +2018,6,3,1542833777,1185709878,false +2018,6,3,900351860,2046760656,false +2018,6,3,1835945606,290589519,true +2018,6,4,1267911767,1040176946,false +2018,6,4,59657928,430900664,false +2018,6,4,1457511951,1196159023,true +2018,6,4,262903974,1916509902,false +2018,6,4,484949477,773402896,false +2018,6,4,211947328,386699511,false +2018,6,4,417539993,730513850,true +2018,6,4,1762955499,1371883768,true +2018,6,4,1904686936,612640485,false +2018,6,4,979884970,2081293978,true +2018,6,5,1825788656,303225756,true +2018,6,5,842357997,2105301792,false +2018,6,5,1020355697,274472404,true +2018,6,5,416684604,607649584,true +2018,6,5,1667302073,1874681145,true +2018,6,5,1088959387,1185868866,false +2018,6,5,122159265,354026124,true +2018,6,5,314784199,648480186,true +2018,6,5,379573699,2101170980,true +2018,6,5,1445298585,1904606251,true +2018,6,6,640056338,1991563109,false +2018,6,6,189600567,1861845372,false +2018,6,6,563692472,695874653,true +2018,6,6,881172185,121237223,false +2018,6,6,943379193,1970513849,true +2018,6,6,754405964,1420506630,false +2018,6,6,1012177419,130194127,false +2018,6,6,750997881,1443228779,false +2018,6,6,1928626976,1391975552,true +2018,6,6,1287012759,239240574,false +2018,6,7,176039198,411456266,true +2018,6,7,1294571996,974712964,true +2018,6,7,1617120175,1343630895,false +2018,6,7,1675400632,595058992,true +2018,6,7,477745586,1927028541,true +2018,6,7,1905165323,206149376,true +2018,6,7,1299161915,268414930,true +2018,6,7,819991511,282334296,false +2018,6,7,918278805,749259128,false +2018,6,7,143653454,839118876,true +2018,6,8,892538897,2051256168,true +2018,6,8,1331834720,845305134,true +2018,6,8,1576801927,638214432,false +2018,6,8,1903613191,2114382125,false +2018,6,8,88002244,932603776,false +2018,6,8,1931410179,943671347,true +2018,6,8,1753193069,770949042,false +2018,6,8,162505679,525407634,false +2018,6,8,622031605,2118630929,false +2018,6,8,714475755,21389755,true +2018,6,9,2038752185,149532860,true +2018,6,9,40889299,1397941209,true +2018,6,9,1665953379,271617182,true +2018,6,9,78517821,1277383100,false +2018,6,9,740241177,1255430425,false +2018,6,9,1277286256,241077040,true +2018,6,9,1666099763,97791371,true +2018,6,9,1669176789,2010998230,false +2018,6,9,850796383,1628748140,true +2018,6,9,785355534,966540559,true +2018,6,10,551657089,2126990010,true +2018,6,10,84389437,818875811,true +2018,6,10,3537073,633825248,true +2018,6,10,574804794,1958962980,false +2018,6,10,1245615143,1063004522,false +2018,6,10,207462126,941878593,false +2018,6,10,1811854322,127065374,true +2018,6,10,1059340296,1002443135,false +2018,6,10,1729232076,341831505,false +2018,6,10,1769177290,816589341,true +2018,6,11,839808624,1037103247,false +2018,6,11,156877223,830317823,false +2018,6,11,1239878961,1889246455,false +2018,6,11,256796122,1850990081,false +2018,6,11,1241397711,1183866836,false +2018,6,11,745663046,965649917,true +2018,6,11,331220408,1108261206,false +2018,6,11,1044962372,68803681,true +2018,6,11,829272118,102006146,true +2018,6,11,441841958,512962359,true +2018,6,12,2103563574,397994672,false +2018,6,12,1715157136,960050658,true +2018,6,12,1571664056,1700684579,true +2018,6,12,1543713536,861288703,false +2018,6,12,833749499,964884656,false +2018,6,12,161647388,396174892,true +2018,6,12,1293334533,841761386,false +2018,6,12,284134298,618395813,false +2018,6,12,1036125471,598668228,false +2018,6,12,850025693,99047410,false +2018,6,13,1724074322,1442361782,true +2018,6,13,1430052005,1548346402,false +2018,6,13,430408719,57310650,true +2018,6,13,746753881,1552722751,false +2018,6,13,261897093,639438793,true +2018,6,13,238626265,753608198,true +2018,6,13,492115031,1810722714,true +2018,6,13,1642855393,1377102339,false +2018,6,13,839335590,479156937,false +2018,6,13,1763807828,1020956068,false +2018,6,14,305100210,1346001091,false +2018,6,14,748924435,345904862,true +2018,6,14,1381377410,436541048,false +2018,6,14,1592868655,1246578556,false +2018,6,14,577203792,643666761,false +2018,6,14,328062351,950288940,false +2018,6,14,563068834,1725046193,true +2018,6,14,41156450,475523560,true +2018,6,14,2093659494,2066409870,false +2018,6,14,376883961,83562833,false +2018,6,15,311512940,1524857635,false +2018,6,15,690002138,412197387,true +2018,6,15,1559402492,708380959,true +2018,6,15,959562799,1948148807,false +2018,6,15,253356183,1576772945,true +2018,6,15,212154073,1869581559,true +2018,6,15,783762419,578109137,true +2018,6,15,604640204,1371784549,false +2018,6,15,592397012,588691913,false +2018,6,15,2010385806,1943308574,false +2018,6,16,249226582,1432434717,false +2018,6,16,1613041630,589729650,false +2018,6,16,1928711354,198874020,false +2018,6,16,251489013,660605948,true +2018,6,16,2084045282,455067065,true +2018,6,16,181926883,1864693226,true +2018,6,16,2083261171,1315952959,true +2018,6,16,654291184,12634114,true +2018,6,16,312655576,1764023159,true +2018,6,16,1180078602,321674128,true +2018,6,17,205229990,810966328,true +2018,6,17,1085076647,1945513669,false +2018,6,17,1029504258,1642613090,true +2018,6,17,1069287336,426222388,false +2018,6,17,608179748,788033195,false +2018,6,17,886652701,1514019688,false +2018,6,17,2025420548,715254294,false +2018,6,17,1166649592,635594438,true +2018,6,17,826248918,573437102,false +2018,6,17,2041817876,1487548743,false +2018,6,18,731008486,2115584609,false +2018,6,18,629893144,564150428,false +2018,6,18,236201830,13201675,true +2018,6,18,2033703971,893565669,false +2018,6,18,2009837534,36818456,true +2018,6,18,1088682816,239047425,false +2018,6,18,1903926061,1257129534,true +2018,6,18,2077002516,1534268696,false +2018,6,18,1736148323,374896187,true +2018,6,18,2014275141,283343359,false +2018,6,19,670914526,1821433006,false +2018,6,19,919020420,1568535155,true +2018,6,19,1220235375,1015934029,true +2018,6,19,439618382,571528045,true +2018,6,19,1281573489,2069791535,false +2018,6,19,499887798,304481012,false +2018,6,19,1054976532,985458413,false +2018,6,19,1516216021,807831867,true +2018,6,19,1786194001,832841400,true +2018,6,19,1696303032,399625200,true +2018,6,20,1529154114,1872395194,true +2018,6,20,1166973665,1432455677,true +2018,6,20,1926834377,987774305,false +2018,6,20,1797035193,1769454651,false +2018,6,20,1790559703,582619749,true +2018,6,20,1020923266,1731271607,true +2018,6,20,1280418476,1908244640,true +2018,6,20,1979056553,970049692,true +2018,6,20,2073652968,1543875863,false +2018,6,20,1528880782,624981224,true +2018,6,21,1396404805,1367429829,false +2018,6,21,1213118483,787881072,true +2018,6,21,1971979087,1353613830,true +2018,6,21,1412884634,558286810,true +2018,6,21,986286974,834792371,false +2018,6,21,860164151,532458141,false +2018,6,21,149919093,1948852818,true +2018,6,21,139821371,1067920424,false +2018,6,21,306003028,566077665,false +2018,6,21,618559340,764390074,false +2018,6,22,1253268322,941861414,false +2018,6,22,410406014,2054034065,false +2018,6,22,31499131,1949179025,true +2018,6,22,649136052,480726652,true +2018,6,22,844582915,2010887692,false +2018,6,22,2057992102,378154748,true +2018,6,22,218310477,1949218895,true +2018,6,22,833113249,176110436,true +2018,6,22,392549050,655169936,false +2018,6,22,933607206,1685783755,false +2018,6,23,2005661945,1527262893,true +2018,6,23,1187098984,181310632,false +2018,6,23,1813049644,1411486489,false +2018,6,23,1339401182,1146585445,true +2018,6,23,322498537,480700477,true +2018,6,23,2058638403,112304479,false +2018,6,23,642074439,1301608709,true +2018,6,23,364598169,927158526,true +2018,6,23,982403444,7612367,true +2018,6,23,1760246540,1145461488,true +2018,6,24,1425056146,753441795,true +2018,6,24,1075202246,946461098,true +2018,6,24,840308933,1568061932,true +2018,6,24,575153431,1459318716,false +2018,6,24,923061689,1396955829,false +2018,6,24,1476728183,1000474786,true +2018,6,24,1813279500,776555411,true +2018,6,24,856716880,108758539,true +2018,6,24,722683225,641671814,true +2018,6,24,416370072,171335126,false +2018,6,25,730309603,584696883,true +2018,6,25,1382872982,1807010613,true +2018,6,25,58629897,1938938312,true +2018,6,25,401187953,892395955,true +2018,6,25,2036987395,1313226474,true +2018,6,25,2051565655,356457234,false +2018,6,25,711651753,699592467,true +2018,6,25,920015198,305852726,false +2018,6,25,250928168,499744508,true +2018,6,25,1230511469,1049709475,true +2018,6,26,739153391,523176410,false +2018,6,26,771299277,987565106,false +2018,6,26,1364952641,1438776214,false +2018,6,26,670378296,1548773634,true +2018,6,26,1141436975,63346767,true +2018,6,26,15437432,2146636254,false +2018,6,26,1532309213,1790172868,false +2018,6,26,744737020,332000647,false +2018,6,26,624245866,445724280,false +2018,6,26,327909861,1388156834,true +2018,6,27,1058909395,284660656,true +2018,6,27,1635390333,1352417852,false +2018,6,27,155934363,1286285970,false +2018,6,27,1820233243,341394979,false +2018,6,27,479008048,1305722909,false +2018,6,27,210117849,166663044,true +2018,6,27,2026465597,93647527,true +2018,6,27,510348198,1104242829,true +2018,6,27,449673574,189449507,false +2018,6,27,1243562917,1822901767,false +2018,6,28,1783906337,514596715,false +2018,6,28,1469557710,1891310112,true +2018,6,28,407375282,606675757,true +2018,6,28,1047837516,1509464033,false +2018,6,28,1027501423,591394907,false +2018,6,28,1464426516,1418378750,true +2018,6,28,232866766,1663262000,true +2018,6,28,1240491940,1440299531,true +2018,6,28,57689850,603694361,false +2018,6,28,2087610606,166047028,false +2018,6,29,1489348057,1968125741,true +2018,6,29,1474440658,1198124553,false +2018,6,29,231002569,468899842,true +2018,6,29,1626941379,290342663,true +2018,6,29,1344241027,557673450,false +2018,6,29,1336204896,44004310,false +2018,6,29,693309410,80557585,true +2018,6,29,1950126121,1206964096,false +2018,6,29,911184765,397293037,true +2018,6,29,1250180252,1990218765,false +2018,6,30,239838297,1335359480,true +2018,6,30,1404195933,1246480442,false +2018,6,30,2146538272,747390906,true +2018,6,30,115287860,463229529,false +2018,6,30,810482074,904691837,false +2018,6,30,674113796,724479852,true +2018,6,30,1783553629,365367118,true +2018,6,30,886054416,765531166,false +2018,6,30,514348531,547218450,false +2018,6,30,349600801,1616269618,true +2018,6,31,254336485,755917019,false +2018,6,31,2001838447,1280952169,true +2018,6,31,250514021,389979577,false +2018,6,31,814496599,1996887841,true +2018,6,31,959846809,2023154884,false +2018,6,31,396043845,1778243094,false +2018,6,31,1349199789,1808461079,true +2018,6,31,284722528,1017250314,false +2018,6,31,730572084,556786908,true +2018,6,31,1956027347,1244983616,true +2018,7,1,2023056781,953915862,true +2018,7,1,2072011469,1673345549,false +2018,7,1,1521921335,1213224156,true +2018,7,1,789429896,883228910,false +2018,7,1,2066098272,607656000,false +2018,7,1,1394860544,328032482,false +2018,7,1,281030678,312095267,false +2018,7,1,835302425,41468924,false +2018,7,1,1628360926,2091797273,true +2018,7,1,638530151,813372004,false +2018,7,2,2046242721,2045286066,true +2018,7,2,882289884,850668615,true +2018,7,2,1798095404,1174494909,false +2018,7,2,1101110011,1725710175,true +2018,7,2,1332471477,948837222,false +2018,7,2,791709289,584958749,true +2018,7,2,103795240,566786427,true +2018,7,2,992438264,520441500,true +2018,7,2,1908461348,490104183,true +2018,7,2,1322036084,547529188,true +2018,7,3,521263090,91831601,false +2018,7,3,1303981022,2047488042,false +2018,7,3,1268548577,1864825650,false +2018,7,3,327199973,165989129,false +2018,7,3,552774847,366564904,false +2018,7,3,233041833,1795215086,false +2018,7,3,1255787376,1956690931,false +2018,7,3,1802260537,727314220,true +2018,7,3,1483371226,1273860914,true +2018,7,3,556478029,2140834926,true +2018,7,4,47273614,1009031014,false +2018,7,4,1435569949,516090506,true +2018,7,4,1001547515,812925698,true +2018,7,4,492746612,783987418,false +2018,7,4,2097348380,748380159,false +2018,7,4,786449480,2031257258,false +2018,7,4,1822097357,910938828,true +2018,7,4,1231279483,848038236,true +2018,7,4,272274258,588895415,false +2018,7,4,1719959962,450120780,false +2018,7,5,1600864186,991291934,false +2018,7,5,1639076523,1606416034,true +2018,7,5,89028897,779042313,true +2018,7,5,201444211,705327503,false +2018,7,5,1119165690,1697808382,false +2018,7,5,228800949,1398136233,true +2018,7,5,1678030313,1277049388,false +2018,7,5,1337701717,1892429921,true +2018,7,5,1445407708,1612031300,true +2018,7,5,185101680,499753164,false +2018,7,6,247801437,118264978,false +2018,7,6,1979685821,661393709,false +2018,7,6,1135343443,20840505,true +2018,7,6,43611094,471103244,false +2018,7,6,952496818,2036352161,false +2018,7,6,1931245375,941094335,true +2018,7,6,620475332,1240544625,true +2018,7,6,483594301,1992523682,false +2018,7,6,747735823,266661809,false +2018,7,6,1972962529,1023078523,true +2018,7,7,845494287,1637792230,true +2018,7,7,187992274,868990915,true +2018,7,7,6535218,2033617478,true +2018,7,7,1333542841,1428894655,false +2018,7,7,473917660,1671481942,true +2018,7,7,1754182491,49510067,false +2018,7,7,2133167199,1963652239,false +2018,7,7,974874570,945595798,true +2018,7,7,1768718888,1179026399,true +2018,7,7,1268655406,1961161741,false +2018,7,8,1208369295,730705439,true +2018,7,8,1446777371,27976606,true +2018,7,8,1893526287,1239505368,true +2018,7,8,1017848429,1418157137,true +2018,7,8,269568469,1494613227,false +2018,7,8,97517081,521388860,false +2018,7,8,1548713948,305882355,false +2018,7,8,547825796,2135820207,false +2018,7,8,1894789908,1389437828,true +2018,7,8,40086575,1504549374,false +2018,7,9,1038712399,346245008,true +2018,7,9,628699108,638543635,true +2018,7,9,981534431,881296021,false +2018,7,9,348647844,2055395920,false +2018,7,9,1927839790,1709024100,true +2018,7,9,1931411713,182492017,true +2018,7,9,307254705,1101840800,false +2018,7,9,2093614517,763809892,false +2018,7,9,300232718,789273675,false +2018,7,9,1597716618,2074815315,true +2018,7,10,412623951,598760181,true +2018,7,10,1412744180,1066240859,true +2018,7,10,1696441443,1680811114,true +2018,7,10,1003804393,1123451552,false +2018,7,10,1717784185,138749684,false +2018,7,10,739844063,164839097,false +2018,7,10,1196068039,2075397513,false +2018,7,10,297170682,183445679,false +2018,7,10,673130968,592691844,true +2018,7,10,1874527684,1974315016,true +2018,7,11,635084168,1365908400,true +2018,7,11,1111525391,542234764,true +2018,7,11,1266382116,214232275,true +2018,7,11,448472277,834421963,false +2018,7,11,1549002101,35460352,false +2018,7,11,518203172,902358922,false +2018,7,11,725278614,1099315280,true +2018,7,11,1634557575,2006082725,false +2018,7,11,743111145,343214804,false +2018,7,11,219247456,985572874,true +2018,7,12,532984974,2086447845,false +2018,7,12,536794928,698901451,false +2018,7,12,1243358488,1500993560,false +2018,7,12,932805068,887596682,true +2018,7,12,874674052,92678522,true +2018,7,12,1348330464,1973393831,true +2018,7,12,43713178,2013401321,true +2018,7,12,555816023,1746147057,false +2018,7,12,349862964,991152807,true +2018,7,12,889278848,1791936462,false +2018,7,13,2139174453,1950031727,false +2018,7,13,173627402,913283097,true +2018,7,13,1292551942,1183607992,false +2018,7,13,552845610,1222841637,true +2018,7,13,1908746480,1399294455,false +2018,7,13,143093384,846938014,false +2018,7,13,109429180,1778920167,false +2018,7,13,1361778120,1784930638,true +2018,7,13,544077909,1463197268,true +2018,7,13,965408457,905818087,false +2018,7,14,1203540063,1456639446,true +2018,7,14,695414512,592837721,false +2018,7,14,1781662726,109051511,false +2018,7,14,818891032,1008824946,false +2018,7,14,1715016299,1157272395,false +2018,7,14,357461247,1230311081,false +2018,7,14,2121385347,637717069,true +2018,7,14,1473657418,725745764,true +2018,7,14,1921768506,1296886063,true +2018,7,14,73775732,1082146084,false +2018,7,15,1764810298,1864327898,true +2018,7,15,2089302592,180102481,false +2018,7,15,1797305519,2034628553,true +2018,7,15,1874138767,1137268127,false +2018,7,15,1808500688,1175224979,true +2018,7,15,1919871392,1555474504,false +2018,7,15,983982884,1440126775,false +2018,7,15,2043731242,1741950857,false +2018,7,15,328127151,837527297,false +2018,7,15,1773437648,1904502563,false +2018,7,16,1826412949,832539693,true +2018,7,16,1051412439,1373449430,true +2018,7,16,240017105,681576339,true +2018,7,16,846443059,741953549,false +2018,7,16,636421901,1154295795,true +2018,7,16,1869145147,1482131247,true +2018,7,16,231897803,1537643972,false +2018,7,16,1808841128,1687151957,false +2018,7,16,490462801,1664984985,true +2018,7,16,1247148971,1565480280,false +2018,7,17,868603165,329564782,false +2018,7,17,1472068416,328021874,true +2018,7,17,148659404,945434236,false +2018,7,17,1059989509,326251746,true +2018,7,17,1122118585,53937261,true +2018,7,17,225658686,1823310166,true +2018,7,17,998940092,1917024254,true +2018,7,17,971149777,2048181265,true +2018,7,17,1572840914,2146917496,true +2018,7,17,1086359893,1057739512,false +2018,7,18,1863337370,1458084994,true +2018,7,18,992121752,1928648720,true +2018,7,18,1243268070,805261884,false +2018,7,18,448901406,925595165,false +2018,7,18,1515246031,76514726,true +2018,7,18,1801305732,839490388,false +2018,7,18,1189155343,1536173754,true +2018,7,18,1548939735,1158281574,true +2018,7,18,1470011541,931392965,false +2018,7,18,67468345,78717006,true +2018,7,19,1981614214,1531918307,false +2018,7,19,2067379555,1496124832,true +2018,7,19,1720681770,1397425836,true +2018,7,19,1354746355,775238741,false +2018,7,19,137217657,753127793,true +2018,7,19,1132977790,1622647111,false +2018,7,19,674815037,908010395,false +2018,7,19,809748704,1278705362,false +2018,7,19,1171755790,1862199049,false +2018,7,19,2123118145,1383458396,false +2018,7,20,1305973906,2094531200,false +2018,7,20,670834624,1515665582,false +2018,7,20,1088835768,1009546127,true +2018,7,20,1391206614,1468018281,true +2018,7,20,347410424,1661046895,true +2018,7,20,260729276,1234241529,true +2018,7,20,2039697082,646019529,true +2018,7,20,898102652,667994321,true +2018,7,20,1724792941,1107051878,false +2018,7,20,253898371,162778076,false +2018,7,21,937698553,109634103,false +2018,7,21,1808569790,1211191133,false +2018,7,21,715490703,1641509695,false +2018,7,21,514400171,2047567041,false +2018,7,21,1846052358,855421490,true +2018,7,21,2102534128,293080325,true +2018,7,21,502648390,979837941,false +2018,7,21,417175367,643089601,false +2018,7,21,1503212396,1444960324,false +2018,7,21,258005227,326003452,true +2018,7,22,989317808,1780769563,true +2018,7,22,2125697989,1986843726,true +2018,7,22,398622775,1983121863,true +2018,7,22,678078729,908431359,true +2018,7,22,1590704717,1710231194,false +2018,7,22,551791882,238417697,false +2018,7,22,974720453,1773694401,false +2018,7,22,1190709347,1429642718,true +2018,7,22,2110825068,806718123,true +2018,7,22,2039398096,1478901095,false +2018,7,23,156741329,324342014,true +2018,7,23,775583306,1068513611,false +2018,7,23,1068628182,288093461,false +2018,7,23,857490496,1947372840,true +2018,7,23,1590795453,493576047,false +2018,7,23,1166727172,126044131,false +2018,7,23,119213753,1240576936,true +2018,7,23,1297653963,760827895,true +2018,7,23,1463577376,2012421827,true +2018,7,23,2133416649,1113395323,false +2018,7,24,484100055,1295481337,false +2018,7,24,1670632014,1674146016,true +2018,7,24,131687825,91006532,true +2018,7,24,1614886141,1581590956,false +2018,7,24,1954572783,732242527,true +2018,7,24,525879398,2043197071,false +2018,7,24,1815495602,1522552820,true +2018,7,24,142205615,1950999202,true +2018,7,24,229281900,1003746862,false +2018,7,24,2021973265,1821296062,false +2018,7,25,2096717695,1661591273,false +2018,7,25,1692645714,1028002337,false +2018,7,25,666865741,254230835,true +2018,7,25,375663238,285759457,false +2018,7,25,2041960921,13752037,true +2018,7,25,409530042,244807641,true +2018,7,25,1963481173,708136048,false +2018,7,25,1124514390,1835037204,false +2018,7,25,114841934,1263927609,true +2018,7,25,1983688538,1354046443,true +2018,7,26,1654200692,364387353,true +2018,7,26,871156164,2089003076,false +2018,7,26,530403115,1702730460,true +2018,7,26,810491837,1715034299,true +2018,7,26,1548829556,1713278160,false +2018,7,26,1982832482,309573789,false +2018,7,26,955117253,1043130311,false +2018,7,26,1876321602,1365474041,false +2018,7,26,868655592,456371228,false +2018,7,26,303672812,506465077,false +2018,7,27,792281860,606021524,false +2018,7,27,1740771868,1452352491,false +2018,7,27,1315284306,1647011888,true +2018,7,27,934805609,1016193527,false +2018,7,27,86183473,1266493271,false +2018,7,27,1672450186,1442306557,true +2018,7,27,1454580884,435745498,true +2018,7,27,883338112,1459700946,true +2018,7,27,225599489,1045556411,false +2018,7,27,1358129491,2084270987,false +2018,7,28,111813273,526676937,false +2018,7,28,141377561,1301935650,false +2018,7,28,1044778466,1007040097,false +2018,7,28,230982021,1117669358,true +2018,7,28,1911186500,455271939,false +2018,7,28,426736437,645266271,true +2018,7,28,813529477,208588150,true +2018,7,28,1405856622,2058638021,true +2018,7,28,2115166874,1803304102,true +2018,7,28,2114039567,798852613,false +2018,7,29,850940332,33088659,true +2018,7,29,445327067,1895478661,false +2018,7,29,41248043,68233951,true +2018,7,29,752329075,2127710403,true +2018,7,29,93337909,1364918234,true +2018,7,29,847178896,465126212,true +2018,7,29,781111494,1414736680,false +2018,7,29,453499742,1582204135,true +2018,7,29,1694204008,2099782222,true +2018,7,29,1455415560,174326466,false +2018,7,30,1016723037,528392646,false +2018,7,30,897361367,1742625802,false +2018,7,30,861953150,234241139,false +2018,7,30,1603881582,59691705,false +2018,7,30,1265167926,983578242,true +2018,7,30,1562849293,1152659960,false +2018,7,30,470042994,1572345894,true +2018,7,30,1344349660,1185782887,false +2018,7,30,1018383188,94618023,false +2018,7,30,836803250,4139746,true +2018,7,31,779321868,1270234559,true +2018,7,31,2059483091,2039464025,false +2018,7,31,729133862,786196541,false +2018,7,31,1557652631,1667043783,true +2018,7,31,293124650,662277733,false +2018,7,31,402125819,1376102778,false +2018,7,31,219784558,69387140,false +2018,7,31,33223817,2076128032,true +2018,7,31,1820690258,691900337,false +2018,7,31,783128605,1621865052,false +2018,8,1,941785203,1020211124,false +2018,8,1,355744875,1837365195,false +2018,8,1,1749033846,123708550,true +2018,8,1,965416541,78497989,true +2018,8,1,1213140331,1539022323,false +2018,8,1,232632725,1662864443,true +2018,8,1,881554951,2130423600,true +2018,8,1,1208855228,1874557821,false +2018,8,1,1656561955,2069464470,false +2018,8,1,497882732,1574838237,true +2018,8,2,1376423741,1404950716,false +2018,8,2,266024910,1469341271,true +2018,8,2,2028412145,386257053,true +2018,8,2,1776079171,406292685,true +2018,8,2,286851493,1965324696,true +2018,8,2,845203127,97579135,false +2018,8,2,1737055196,526245737,false +2018,8,2,1253035121,1846040507,false +2018,8,2,1588970987,792485739,true +2018,8,2,2001061485,420770051,false +2018,8,3,1256183817,1655570368,true +2018,8,3,1072044979,2007509015,false +2018,8,3,1261343978,1603071030,true +2018,8,3,352052777,451431844,true +2018,8,3,75142249,1910698755,false +2018,8,3,1300980420,1573468292,true +2018,8,3,1555424242,1571867308,true +2018,8,3,1626122030,1432491998,true +2018,8,3,744593057,754278318,false +2018,8,3,83310906,1596626812,false +2018,8,4,196472665,658191310,false +2018,8,4,263913918,1235299381,false +2018,8,4,1067941178,1657752081,true +2018,8,4,119736228,295596096,true +2018,8,4,1978105669,1766392267,false +2018,8,4,251705689,778663875,true +2018,8,4,505150987,1735454798,true +2018,8,4,954210786,1717470211,true +2018,8,4,1379095224,1958218775,true +2018,8,4,337394745,1122306047,true +2018,8,5,1741543651,47949203,false +2018,8,5,1413210821,1112218455,true +2018,8,5,1349606620,1035372746,true +2018,8,5,978135563,2133217488,false +2018,8,5,766904954,323389592,true +2018,8,5,499100904,901708760,false +2018,8,5,1467039889,829386555,true +2018,8,5,214097391,681661650,true +2018,8,5,528239514,2023371099,false +2018,8,5,628372685,1096625687,true +2018,8,6,1872475701,1581783005,true +2018,8,6,312580019,515513320,false +2018,8,6,1728522378,1164292635,true +2018,8,6,2078861371,501456842,false +2018,8,6,858572724,1370395994,false +2018,8,6,1923096651,1358636035,true +2018,8,6,28851387,95455123,true +2018,8,6,942786263,954199601,false +2018,8,6,1556287184,1394574062,false +2018,8,6,1962476948,1741076529,false +2018,8,7,1930387127,226120825,true +2018,8,7,639508481,1600251084,true +2018,8,7,872964529,739066886,false +2018,8,7,339724618,1336039913,false +2018,8,7,1161152582,1789035575,true +2018,8,7,1091938802,1186036795,true +2018,8,7,1154259376,1492250905,false +2018,8,7,1400610631,1356048534,false +2018,8,7,501516002,530002151,false +2018,8,7,156282387,2045011317,true +2018,8,8,1229676640,1850764742,true +2018,8,8,821466879,1462399842,true +2018,8,8,829931614,1758193813,true +2018,8,8,1271088800,1714515246,false +2018,8,8,851586783,1174430767,false +2018,8,8,616123448,1657004356,true +2018,8,8,386570194,1305882032,false +2018,8,8,575859070,1464746882,true +2018,8,8,640936796,51650965,true +2018,8,8,519076622,277797709,false +2018,8,9,1064156874,1265028818,false +2018,8,9,2131278937,1161720806,true +2018,8,9,1685836008,1983287349,true +2018,8,9,1086001959,849487151,false +2018,8,9,1911357103,1147957681,true +2018,8,9,992636418,1040226641,true +2018,8,9,1726057857,1993001570,true +2018,8,9,6071858,1799933967,true +2018,8,9,1566983797,1102900581,false +2018,8,9,1471582472,1673075367,true +2018,8,10,1652343388,1738408676,true +2018,8,10,1539919270,877261708,false +2018,8,10,913246342,401625053,false +2018,8,10,1357951893,597162854,false +2018,8,10,1998475891,501202576,true +2018,8,10,288711609,614258960,true +2018,8,10,74118383,1843505820,false +2018,8,10,1203024011,922848404,false +2018,8,10,141175632,2140379944,true +2018,8,10,1342483359,1135805782,true +2018,8,11,868834250,181338808,false +2018,8,11,2054157791,1069025768,true +2018,8,11,1267495961,1762336629,false +2018,8,11,1415693573,1493552189,false +2018,8,11,459064300,1553137684,false +2018,8,11,2049096757,2099670840,false +2018,8,11,130109147,946688163,true +2018,8,11,540018381,585456332,true +2018,8,11,1110081999,2083534813,true +2018,8,11,384461758,482021923,false +2018,8,12,637442075,2015693021,true +2018,8,12,1546591499,1591960454,true +2018,8,12,174480066,606023236,true +2018,8,12,1217505870,1975176191,true +2018,8,12,582176923,914246442,true +2018,8,12,256443968,1658575087,false +2018,8,12,835747920,935684986,false +2018,8,12,1941556929,1515962373,true +2018,8,12,503356778,386273415,true +2018,8,12,392186838,1842504287,true +2018,8,13,1603956718,695025649,true +2018,8,13,816100752,1485375932,true +2018,8,13,1766623791,2057783877,false +2018,8,13,1372426716,1037793031,false +2018,8,13,1351713512,1531122641,true +2018,8,13,1991586478,1689182115,false +2018,8,13,2128017847,1665340836,false +2018,8,13,1901124099,844385089,false +2018,8,13,2077506189,59137465,true +2018,8,13,1420270761,479786531,true +2018,8,14,659386556,400645297,true +2018,8,14,1053562329,1376058397,true +2018,8,14,822711450,998350007,true +2018,8,14,847981823,134426292,true +2018,8,14,1014671096,1691508829,false +2018,8,14,820634692,1638704424,false +2018,8,14,1831859173,658370954,true +2018,8,14,1156067773,1950277454,true +2018,8,14,1834682987,2020665311,false +2018,8,14,1873474,1797737599,true +2018,8,15,954427723,545534743,false +2018,8,15,427707486,142425619,false +2018,8,15,1230479593,1726484812,false +2018,8,15,1466357472,773443861,false +2018,8,15,1655793884,1842780713,true +2018,8,15,2061580771,1294581050,false +2018,8,15,879555559,1588526964,true +2018,8,15,308318840,588793665,true +2018,8,15,1844590405,1424187502,true +2018,8,15,1244782639,981820223,true +2018,8,16,1993601670,1503125759,false +2018,8,16,1294465327,533688204,true +2018,8,16,49191805,717352623,false +2018,8,16,310680741,631663433,false +2018,8,16,111080929,1687620809,false +2018,8,16,1856485550,270152633,true +2018,8,16,68329652,1650030158,true +2018,8,16,829755397,123582940,true +2018,8,16,1122325396,2013933435,true +2018,8,16,1880148256,1398298133,false +2018,8,17,1708422264,1574507013,true +2018,8,17,1764942723,579727808,true +2018,8,17,1556884109,1283901035,true +2018,8,17,2027383148,18071296,false +2018,8,17,1342745898,1785645533,false +2018,8,17,1933198121,1333146929,true +2018,8,17,87774744,1515348917,false +2018,8,17,338699957,1917195452,true +2018,8,17,2145092769,548148846,true +2018,8,17,1563298159,60088136,false +2018,8,18,2090447653,2094954394,false +2018,8,18,1194527503,1738574198,false +2018,8,18,2110837533,1468251990,true +2018,8,18,1009309721,978633130,true +2018,8,18,1455694812,2100599576,true +2018,8,18,1018187651,1733561235,false +2018,8,18,1933760110,918958331,true +2018,8,18,1939184208,1576120943,false +2018,8,18,383188323,2138019787,true +2018,8,18,144911685,138429501,false +2018,8,19,991564134,666281349,true +2018,8,19,1148660740,975664725,true +2018,8,19,1318177774,627616004,false +2018,8,19,1078766132,1399066669,true +2018,8,19,2049363836,704953622,false +2018,8,19,775561858,1737511718,true +2018,8,19,844732681,1424470568,true +2018,8,19,949582996,1495948986,true +2018,8,19,1893669057,1348690252,false +2018,8,19,1811608145,1465308775,true +2018,8,20,1197875235,1462265420,false +2018,8,20,835412487,620405539,false +2018,8,20,1921655305,358551546,false +2018,8,20,1440083538,390634563,true +2018,8,20,330997083,1537580302,false +2018,8,20,1890551250,546063385,true +2018,8,20,693405010,775612706,false +2018,8,20,1855839255,1051599543,false +2018,8,20,1365975694,1712804021,false +2018,8,20,1111020110,1746974097,false +2018,8,21,61263480,834477111,true +2018,8,21,1886984580,1892545791,true +2018,8,21,1318821537,1889993100,true +2018,8,21,22956459,1664624840,false +2018,8,21,1503799325,590385297,true +2018,8,21,1067749284,183483200,false +2018,8,21,1004405516,1691449980,true +2018,8,21,1742914029,1892744856,false +2018,8,21,1511863506,1639638321,false +2018,8,21,121040590,2102262390,false +2018,8,22,22623144,1350316776,true +2018,8,22,208445472,218785786,true +2018,8,22,2031968395,1757094838,false +2018,8,22,561377273,1491832993,true +2018,8,22,1119005882,1991231950,true +2018,8,22,1649503967,1716969691,true +2018,8,22,383202882,1517732511,false +2018,8,22,1096980316,871643436,true +2018,8,22,1750141760,359683611,true +2018,8,22,619867141,1821077796,false +2018,8,23,435848681,975688387,true +2018,8,23,248558666,1668130206,true +2018,8,23,1685447547,965707855,false +2018,8,23,951871922,1917116420,false +2018,8,23,1119184534,550007822,false +2018,8,23,921246285,1875441284,false +2018,8,23,2049020349,2136201554,false +2018,8,23,2047778433,596919680,true +2018,8,23,430661866,1341975934,false +2018,8,23,27086721,714776734,true +2018,8,24,888402293,682312342,false +2018,8,24,230244615,1776854590,false +2018,8,24,1734102467,1669268574,false +2018,8,24,781626418,44108361,false +2018,8,24,445195896,2023177070,true +2018,8,24,1252919952,251364750,false +2018,8,24,2036380962,850030273,true +2018,8,24,315238329,143682861,true +2018,8,24,694805650,2094200749,false +2018,8,24,404880661,895654331,true +2018,8,25,1404549471,1149467970,false +2018,8,25,1213550228,2132068392,true +2018,8,25,634052941,195306370,false +2018,8,25,1375799425,1603341968,false +2018,8,25,23386764,805722106,false +2018,8,25,1440751045,190901626,true +2018,8,25,1531403265,697407636,false +2018,8,25,1047620390,2076639790,false +2018,8,25,285875750,1677617858,false +2018,8,25,1454554989,1974729009,false +2018,8,26,582936605,849528922,true +2018,8,26,92137850,787359756,false +2018,8,26,2084923139,1012063403,true +2018,8,26,842884265,2059189208,true +2018,8,26,382133685,705550268,false +2018,8,26,924006679,938393167,false +2018,8,26,597559672,2044604090,true +2018,8,26,1715189932,468510044,false +2018,8,26,1307857996,454666224,false +2018,8,26,1586379506,1121993037,true +2018,8,27,419555274,1020026345,false +2018,8,27,1394911517,367262782,true +2018,8,27,987950051,1538541711,false +2018,8,27,761651418,1306364252,false +2018,8,27,1617928920,2069636187,true +2018,8,27,612007859,626814208,false +2018,8,27,1821026370,504729334,true +2018,8,27,54843956,1920220896,true +2018,8,27,1572186886,654128606,false +2018,8,27,386828559,1421032905,false +2018,8,28,2020422278,670617317,false +2018,8,28,1080845301,82038932,false +2018,8,28,1734622573,861836199,false +2018,8,28,61261018,41759323,false +2018,8,28,1772672235,1288171602,true +2018,8,28,1924847584,937973785,true +2018,8,28,108173633,89477961,false +2018,8,28,770399008,1289907255,true +2018,8,28,1582228700,968711822,false +2018,8,28,1030608642,1860311229,false +2018,8,29,803600770,1440052471,true +2018,8,29,458543852,971756303,false +2018,8,29,767020857,147426459,false +2018,8,29,1659972459,1795851405,false +2018,8,29,910368487,1321444603,false +2018,8,29,1430472229,1254871404,true +2018,8,29,1496789736,1848380108,true +2018,8,29,1040235823,1096020893,true +2018,8,29,200898056,1869612150,false +2018,8,29,1639059111,733360545,false +2018,8,30,1657189791,779053471,false +2018,8,30,1062549148,1741257111,false +2018,8,30,42542538,340743514,true +2018,8,30,1482860709,860956400,true +2018,8,30,1564376816,470967768,true +2018,8,30,1312830397,2030601602,false +2018,8,30,337875472,608773541,true +2018,8,30,1813825087,1080807162,true +2018,8,30,1975420310,1591744991,true +2018,8,30,809900304,83769301,false +2018,8,31,456138400,1424938464,false +2018,8,31,704696415,1656010604,false +2018,8,31,1480858367,806105179,true +2018,8,31,1965616515,1303288509,false +2018,8,31,347422292,2036897496,false +2018,8,31,1898270937,1340421096,false +2018,8,31,407593242,1993823247,true +2018,8,31,1161352117,934553770,false +2018,8,31,610802306,1293597339,true +2018,8,31,336527811,1920766002,true +2018,9,1,1275893346,1997232372,true +2018,9,1,1717304939,2124041504,false +2018,9,1,1397972653,1714877621,false +2018,9,1,1229356562,1844996764,false +2018,9,1,1403085867,910547608,false +2018,9,1,1146698832,259378109,false +2018,9,1,1088418925,1423599020,false +2018,9,1,429772662,515228340,false +2018,9,1,124164909,1141590967,true +2018,9,1,1237677810,1584019846,false +2018,9,2,1506851740,608341434,false +2018,9,2,652432759,1167099948,false +2018,9,2,1619200591,205239063,true +2018,9,2,1137033465,1617163382,true +2018,9,2,1303677635,1414038218,true +2018,9,2,1040630224,1764603402,true +2018,9,2,1708940213,1761448361,true +2018,9,2,1659162537,930235721,false +2018,9,2,1638925252,619906486,false +2018,9,2,827860492,1021199889,true +2018,9,3,1966023693,1640672734,false +2018,9,3,163838875,1880987526,true +2018,9,3,1804273864,63787711,false +2018,9,3,1942507104,393709679,false +2018,9,3,1169657878,1044234311,true +2018,9,3,1991582904,789705675,false +2018,9,3,31710572,1040828749,false +2018,9,3,439150024,1201122216,true +2018,9,3,236739697,500120945,false +2018,9,3,1116476791,1388193886,true +2018,9,4,579616807,311215848,true +2018,9,4,2093096805,1054048450,false +2018,9,4,73270188,790280447,true +2018,9,4,477315416,844909552,false +2018,9,4,1587742719,2131579254,false +2018,9,4,649519381,243058610,true +2018,9,4,1655197324,1926077960,false +2018,9,4,1669921441,1433962164,false +2018,9,4,959910509,1791388854,true +2018,9,4,79670100,1062113287,false +2018,9,5,1229763227,1967339699,false +2018,9,5,717192023,850873761,false +2018,9,5,1365675406,989673214,false +2018,9,5,62372435,1838643670,false +2018,9,5,991963891,1464945937,true +2018,9,5,355163519,1553234947,true +2018,9,5,943712907,556083505,false +2018,9,5,914299991,1437590245,false +2018,9,5,943159114,1478735675,false +2018,9,5,802774678,223934281,true +2018,9,6,1298662474,585963012,false +2018,9,6,197638484,1022118939,true +2018,9,6,969469246,1350357165,true +2018,9,6,900565583,2060765450,false +2018,9,6,267601495,691848306,true +2018,9,6,2057471946,1242202920,false +2018,9,6,1259554786,1753454306,false +2018,9,6,1492330636,1690819417,true +2018,9,6,724951053,1466188157,true +2018,9,6,1956211423,1636981873,true +2018,9,7,1556947337,1974980400,false +2018,9,7,2019953312,1082901322,true +2018,9,7,1708216329,783500583,true +2018,9,7,2000254398,923339071,true +2018,9,7,353436626,1273996262,true +2018,9,7,1507529667,2009417157,true +2018,9,7,70505424,396379824,true +2018,9,7,442798127,1198020136,true +2018,9,7,188103156,1269397155,true +2018,9,7,1317476565,1357152907,true +2018,9,8,995657663,1876171793,true +2018,9,8,1171306616,1950965181,false +2018,9,8,130994508,1052763130,false +2018,9,8,1266185047,491188614,false +2018,9,8,1562198836,790118585,false +2018,9,8,1543921268,500988789,true +2018,9,8,1666675748,1139618884,true +2018,9,8,661185447,874946134,true +2018,9,8,1630895355,730537863,false +2018,9,8,758551537,1415482074,false +2018,9,9,71699142,2126951877,false +2018,9,9,349871740,23481881,true +2018,9,9,215991292,1927216136,false +2018,9,9,765791822,75349746,false +2018,9,9,1028673218,1445968899,true +2018,9,9,800891298,502954874,true +2018,9,9,72194083,570775283,true +2018,9,9,2069968213,625909232,false +2018,9,9,154909820,1408712803,true +2018,9,9,1205316599,191583485,false +2018,9,10,325300111,163703265,false +2018,9,10,853850967,1045163293,false +2018,9,10,1754042105,1185046533,true +2018,9,10,138835110,1995712905,false +2018,9,10,341505022,1282774609,true +2018,9,10,1663400583,950180097,false +2018,9,10,825864683,1737568993,true +2018,9,10,1735982498,2035871542,true +2018,9,10,1914225784,1531110364,false +2018,9,10,1425773464,83682774,false +2018,9,11,392680954,1156757538,false +2018,9,11,1979456869,1760219226,false +2018,9,11,1309624124,1304095157,true +2018,9,11,11274649,643724334,false +2018,9,11,1784025941,228578243,true +2018,9,11,1404279787,966847183,false +2018,9,11,240965608,1523236708,false +2018,9,11,1681086224,799860195,false +2018,9,11,811255418,506763624,false +2018,9,11,1319534313,1977789830,true +2018,9,12,1425268204,853987524,true +2018,9,12,1590868054,74919743,false +2018,9,12,1649382210,265969792,false +2018,9,12,321544531,1608876113,true +2018,9,12,873633585,1974645050,false +2018,9,12,1738942259,313516141,true +2018,9,12,1482144304,1131175320,false +2018,9,12,1583608333,1099932141,true +2018,9,12,58499845,1742817848,true +2018,9,12,115884986,1788670752,false +2018,9,13,163851259,1815330050,false +2018,9,13,1761080817,1039034447,true +2018,9,13,1962826058,872579314,true +2018,9,13,1229235815,1215948930,true +2018,9,13,2117928794,1705474727,true +2018,9,13,866740482,766922545,true +2018,9,13,1778661856,1235791710,false +2018,9,13,1265653622,1349469024,true +2018,9,13,1367895311,1420260136,false +2018,9,13,297704174,775389404,true +2018,9,14,1606352976,486640754,false +2018,9,14,259221383,1976871571,true +2018,9,14,2072172087,1607559740,true +2018,9,14,681599690,972145976,false +2018,9,14,1604790202,2095800218,true +2018,9,14,501081456,577847218,true +2018,9,14,1566922960,286392902,false +2018,9,14,679200265,2042561297,false +2018,9,14,1601760001,704158584,false +2018,9,14,1355656545,366474742,false +2018,9,15,42632844,364044598,false +2018,9,15,570404541,1067696044,false +2018,9,15,171183412,620313567,false +2018,9,15,1450298687,1347679598,false +2018,9,15,1472385493,790789794,false +2018,9,15,282955026,1996048523,true +2018,9,15,59307584,46301970,false +2018,9,15,31160890,1817925220,true +2018,9,15,255173921,1456035621,false +2018,9,15,1421672456,588608380,true +2018,9,16,1554444245,1145762362,true +2018,9,16,1688982448,359448847,false +2018,9,16,1510250616,800293111,false +2018,9,16,1511514481,202383592,true +2018,9,16,1960152726,1762236709,true +2018,9,16,667479879,1742126458,true +2018,9,16,1655337283,853859790,true +2018,9,16,1966428459,1796140122,false +2018,9,16,1038455685,1964841286,true +2018,9,16,1711008326,1382924212,true +2018,9,17,2107627607,471426548,false +2018,9,17,1517788063,584300367,true +2018,9,17,2090062254,407893994,true +2018,9,17,1584200756,2112371769,false +2018,9,17,1064038978,96094378,false +2018,9,17,2074744697,1617623613,false +2018,9,17,1404232722,1172118455,false +2018,9,17,530695859,2109100193,false +2018,9,17,573523413,557047228,true +2018,9,17,1227970217,1493370855,true +2018,9,18,915849174,2048954730,true +2018,9,18,2066570911,1744096392,false +2018,9,18,522951922,1305977957,true +2018,9,18,724649627,1601023711,true +2018,9,18,1343188653,1516630994,true +2018,9,18,1340656226,1976673981,false +2018,9,18,1158400920,2035338085,true +2018,9,18,1495931827,1272046940,true +2018,9,18,1467907459,1594299144,false +2018,9,18,1770910107,395979439,false +2018,9,19,2012907821,715718241,true +2018,9,19,1098261515,1692625727,false +2018,9,19,427617132,4896496,true +2018,9,19,976108016,1610181140,true +2018,9,19,1865402379,288257147,false +2018,9,19,1287118192,1238535776,false +2018,9,19,1432200396,1982634229,true +2018,9,19,695815471,1156038363,true +2018,9,19,1522546509,477854771,false +2018,9,19,652798619,424574238,false +2018,9,20,953865927,699199745,true +2018,9,20,945800364,850553173,true +2018,9,20,1047190235,1873477757,true +2018,9,20,664257453,2057884454,true +2018,9,20,1068148459,2125935035,false +2018,9,20,445691351,445945503,true +2018,9,20,1092412852,1487717154,false +2018,9,20,540753389,749292675,true +2018,9,20,567417628,1063047173,true +2018,9,20,2078301212,450452307,true +2018,9,21,1730953851,2062501980,true +2018,9,21,160974533,674024167,true +2018,9,21,697515457,659173813,true +2018,9,21,2064735232,1051225712,true +2018,9,21,1159558726,863285165,false +2018,9,21,1665625594,1032001848,true +2018,9,21,755354158,1883316115,true +2018,9,21,2130795386,263565446,true +2018,9,21,1013565859,1092810241,true +2018,9,21,1505041196,1222842719,true +2018,9,22,940043601,1095063187,true +2018,9,22,874310542,1213057830,false +2018,9,22,963044331,1207393540,true +2018,9,22,180390542,1182615629,true +2018,9,22,1971250848,289739860,false +2018,9,22,2004746032,686322825,false +2018,9,22,1399407819,1844966341,false +2018,9,22,1050864870,938525774,false +2018,9,22,722834754,1453374169,false +2018,9,22,947088216,499327871,true +2018,9,23,750733327,646806668,false +2018,9,23,893994696,628048274,false +2018,9,23,1024015741,1179248518,false +2018,9,23,1638345826,525156251,false +2018,9,23,963813298,295382009,true +2018,9,23,1862855439,720410873,false +2018,9,23,409145952,1384356190,false +2018,9,23,1575775401,933304733,false +2018,9,23,1343909924,1752235358,false +2018,9,23,634367133,1068948767,true +2018,9,24,91705265,165298812,true +2018,9,24,2066581603,1344485408,false +2018,9,24,1773173467,1356372062,true +2018,9,24,686170963,1917854121,true +2018,9,24,48820249,1173610704,true +2018,9,24,893225662,1626064005,false +2018,9,24,2056258333,389809095,true +2018,9,24,2085331027,259743951,false +2018,9,24,788745102,1050507803,false +2018,9,24,1001906236,538190880,false +2018,9,25,1103452429,25207212,true +2018,9,25,484486518,1696213801,true +2018,9,25,171518466,1664915244,true +2018,9,25,1994144332,1034226081,true +2018,9,25,585185011,1575497927,true +2018,9,25,389952520,99159486,false +2018,9,25,641930826,1120442972,true +2018,9,25,146006058,971999941,true +2018,9,25,467360976,425512718,false +2018,9,25,1333569227,89446987,true +2018,9,26,300744017,334040392,true +2018,9,26,654895353,735446589,true +2018,9,26,443815458,2017212820,true +2018,9,26,1603259179,648756689,false +2018,9,26,2120574334,51069391,true +2018,9,26,151984927,1529865694,false +2018,9,26,1966380533,826394018,false +2018,9,26,1601764798,1851347298,true +2018,9,26,969315,242410059,false +2018,9,26,374435040,1967491339,false +2018,9,27,569005355,1289080598,false +2018,9,27,699076096,288035116,false +2018,9,27,1051584679,1203129620,true +2018,9,27,482763326,782230369,false +2018,9,27,545382157,822908923,true +2018,9,27,1171262797,2060117446,false +2018,9,27,127272502,167827250,false +2018,9,27,1595458577,1112644513,true +2018,9,27,330938386,222156138,false +2018,9,27,2012392734,1415097275,true +2018,9,28,744915623,1813664353,false +2018,9,28,326724706,464893666,true +2018,9,28,1938180846,77161888,false +2018,9,28,18483336,1142154581,false +2018,9,28,1649097710,534016587,false +2018,9,28,1945248780,1352801272,false +2018,9,28,1567360991,515173217,false +2018,9,28,1139799283,1778713259,true +2018,9,28,1958449987,1566305537,false +2018,9,28,1282461078,1870297887,true +2018,9,29,667755060,2097236621,true +2018,9,29,860829207,697166830,false +2018,9,29,1672093266,323252923,true +2018,9,29,1037916491,447374829,false +2018,9,29,1416402974,1839612416,false +2018,9,29,81277244,252852720,true +2018,9,29,1278824612,591124690,false +2018,9,29,1908686491,1511236471,false +2018,9,29,1182298299,1018011311,false +2018,9,29,103283634,1908132086,false +2018,9,30,1821878605,442635258,false +2018,9,30,749893303,1596984514,false +2018,9,30,529095777,619627520,true +2018,9,30,1934024847,1577095616,true +2018,9,30,1675636952,1385225668,false +2018,9,30,2140091495,1782873452,false +2018,9,30,1869488936,1074693482,false +2018,9,30,798538153,1413149910,true +2018,9,30,2114081648,1520902484,true +2018,9,30,2001392943,1380754587,false +2018,9,31,1921879483,291933518,true +2018,9,31,189446099,429777940,true +2018,9,31,1980130128,510757727,false +2018,9,31,1063372707,1987953190,true +2018,9,31,476689503,1073925397,false +2018,9,31,757560924,1330812537,false +2018,9,31,1992445805,508221800,true +2018,9,31,473260807,866163715,false +2018,9,31,899407940,1096462986,false +2018,9,31,119764421,1977463340,false +2018,10,1,662461026,1246479419,true +2018,10,1,1331424298,1095841777,false +2018,10,1,683174668,1312759418,true +2018,10,1,1108957633,407247056,true +2018,10,1,1867505051,1343988138,false +2018,10,1,2130985861,263005543,true +2018,10,1,1714309274,786782346,false +2018,10,1,2119075443,1696580106,false +2018,10,1,195716294,73931144,false +2018,10,1,866187407,492790716,false +2018,10,2,1722165021,658668633,false +2018,10,2,1907751139,970018895,false +2018,10,2,606449015,217811427,true +2018,10,2,1099021923,344666264,true +2018,10,2,199315805,2068978646,false +2018,10,2,2109765314,7031795,false +2018,10,2,1151162139,764868230,true +2018,10,2,1343323542,1662339738,true +2018,10,2,1675673412,1218843363,false +2018,10,2,337539424,276050032,true +2018,10,3,1977962271,324902830,true +2018,10,3,861568850,477459531,false +2018,10,3,2138112176,1895614508,false +2018,10,3,1332295658,157595635,false +2018,10,3,2050508129,1208327179,false +2018,10,3,2138664375,818955075,false +2018,10,3,777514616,1708545857,true +2018,10,3,1098274024,1215566125,false +2018,10,3,882095319,174199291,false +2018,10,3,1712006515,1658554266,true +2018,10,4,2123189693,676880379,false +2018,10,4,1080111862,1157884760,false +2018,10,4,1040272510,880339970,true +2018,10,4,1285592224,742250386,true +2018,10,4,1935659379,1485010701,false +2018,10,4,610034883,1303938184,true +2018,10,4,830719110,57615501,true +2018,10,4,505357023,1123289987,false +2018,10,4,460072495,1275884521,true +2018,10,4,106769479,105981493,false +2018,10,5,1665100732,174851304,false +2018,10,5,418078386,1376630279,true +2018,10,5,857470003,626464726,true +2018,10,5,1374756169,1105837061,true +2018,10,5,1908734264,979694462,true +2018,10,5,2114907166,1627645030,true +2018,10,5,54461367,257346127,false +2018,10,5,1850144529,816738526,true +2018,10,5,1566535239,886091088,false +2018,10,5,997847169,1330361156,true +2018,10,6,2054745978,549283487,true +2018,10,6,183046505,158760492,true +2018,10,6,1361843355,1909065400,false +2018,10,6,1649113009,969966473,true +2018,10,6,1525813642,2032096954,false +2018,10,6,1058601178,1098094960,true +2018,10,6,1490412722,309301086,true +2018,10,6,1525911127,1710742417,true +2018,10,6,641373776,718239582,false +2018,10,6,1181292069,1416080394,false +2018,10,7,1951129732,1502524795,true +2018,10,7,2034568587,1173569686,false +2018,10,7,232448480,762085739,false +2018,10,7,895122688,283296072,false +2018,10,7,2096377457,640042062,false +2018,10,7,1936479152,2092078170,true +2018,10,7,434418559,1554838655,true +2018,10,7,480919059,874993327,true +2018,10,7,234094698,944069084,true +2018,10,7,763061905,688112409,true +2018,10,8,1581036404,1579531990,false +2018,10,8,752539698,1924889225,false +2018,10,8,392598366,1753296876,false +2018,10,8,554908348,798150062,false +2018,10,8,360994580,1557339744,true +2018,10,8,346268892,1403086000,false +2018,10,8,990630631,267211842,false +2018,10,8,2061835295,1188002820,true +2018,10,8,1405634312,55736100,false +2018,10,8,2005490675,787641753,false +2018,10,9,1548840246,158280731,false +2018,10,9,1737913314,108807104,false +2018,10,9,1839182818,1665080021,false +2018,10,9,2100824766,1130612644,false +2018,10,9,232743740,816766697,true +2018,10,9,1144809349,320089901,false +2018,10,9,1216169185,1782791603,false +2018,10,9,25696797,1934868130,false +2018,10,9,1749246235,1712129104,true +2018,10,9,1164709178,1535889512,false +2018,10,10,1490193664,1097438372,false +2018,10,10,1470447627,2141471144,true +2018,10,10,1345612492,489009505,false +2018,10,10,805253508,41127117,false +2018,10,10,1351823941,307452050,true +2018,10,10,1003976794,1595247380,true +2018,10,10,1265805586,600228118,true +2018,10,10,1902963216,2054147233,false +2018,10,10,1323119432,90811143,true +2018,10,10,1848860842,1649523421,true +2018,10,11,163406494,1293448206,false +2018,10,11,3174868,764795649,true +2018,10,11,240121602,758159368,false +2018,10,11,1480102826,564238111,true +2018,10,11,402353540,1196213130,false +2018,10,11,678431334,398623243,true +2018,10,11,355209944,181219631,false +2018,10,11,2137120659,395781863,false +2018,10,11,1992336606,1018889128,true +2018,10,11,992534175,2041687883,true +2018,10,12,787608296,1876393484,false +2018,10,12,707032034,1444614971,false +2018,10,12,924135103,991604959,false +2018,10,12,1876173424,1841081448,true +2018,10,12,220057747,2129935805,true +2018,10,12,1796235861,1735785070,true +2018,10,12,1359584859,1339655355,false +2018,10,12,1459940357,1945024527,true +2018,10,12,1377018700,816952780,true +2018,10,12,32902953,1298604450,false +2018,10,13,729103988,244198873,true +2018,10,13,690323992,299240224,false +2018,10,13,680392607,167643721,true +2018,10,13,207711378,996028574,false +2018,10,13,460984328,1636452133,false +2018,10,13,1820203928,2136577560,false +2018,10,13,2033113221,377805609,false +2018,10,13,279256443,1488839635,true +2018,10,13,1632913721,1137404499,false +2018,10,13,1112427642,1965687487,true +2018,10,14,292173543,1888821982,true +2018,10,14,1616173951,854188201,false +2018,10,14,114385020,1648654749,true +2018,10,14,1094556639,2136917523,false +2018,10,14,1361410928,1528795703,false +2018,10,14,1404025896,1594497039,true +2018,10,14,1053618182,1206746300,true +2018,10,14,889126759,2008436340,false +2018,10,14,290991768,505996666,false +2018,10,14,1180184935,1596089154,true +2018,10,15,75496820,662285608,false +2018,10,15,1022769109,1258461713,true +2018,10,15,871752430,1542647487,true +2018,10,15,839967060,396183261,false +2018,10,15,1367962042,775982483,true +2018,10,15,1732070116,80331155,true +2018,10,15,514968998,442651024,false +2018,10,15,222998823,1475676194,true +2018,10,15,849136974,152053924,false +2018,10,15,399742569,1867268872,true +2018,10,16,620804451,1644571583,false +2018,10,16,1415683959,754876552,true +2018,10,16,955671701,2089014001,false +2018,10,16,1447508138,1429805330,true +2018,10,16,610156358,2011309959,true +2018,10,16,481511858,1404918661,true +2018,10,16,2052239502,442240063,false +2018,10,16,1141688830,429517392,true +2018,10,16,1183216184,837989094,false +2018,10,16,1535326428,1255778973,false +2018,10,17,1181777854,1131502589,true +2018,10,17,1144562597,841997581,true +2018,10,17,575498356,1301698618,true +2018,10,17,1596346764,889912236,true +2018,10,17,6034267,896877823,false +2018,10,17,1067986922,1825823528,false +2018,10,17,1531072343,332821531,true +2018,10,17,1785725732,844455280,false +2018,10,17,1059288252,1678355576,false +2018,10,17,1746993015,1773359683,true +2018,10,18,614200684,1194640505,false +2018,10,18,538514000,654900826,true +2018,10,18,1700957953,822628410,true +2018,10,18,515809273,112507952,true +2018,10,18,1397290675,443190518,false +2018,10,18,17898178,1616204182,true +2018,10,18,898619647,1063331889,false +2018,10,18,1601662965,338525080,false +2018,10,18,1202059059,1286163589,true +2018,10,18,1419846700,1563223641,true +2018,10,19,1247320942,1912590109,true +2018,10,19,969665513,345823470,false +2018,10,19,663787456,2141995075,true +2018,10,19,1731651198,426728811,true +2018,10,19,317595478,602754703,true +2018,10,19,1461399719,1352018836,false +2018,10,19,260968717,730148757,false +2018,10,19,1138596194,1595091188,true +2018,10,19,455299255,463792679,false +2018,10,19,2128260846,161905484,true +2018,10,20,1674092789,1959148932,false +2018,10,20,1611819765,1335427723,true +2018,10,20,585408889,412075532,false +2018,10,20,290344010,990914017,false +2018,10,20,1107244550,280869717,true +2018,10,20,956824007,1974420349,true +2018,10,20,1210726547,1779729581,false +2018,10,20,1872325934,1374770454,true +2018,10,20,845612367,1109932141,false +2018,10,20,265121301,1305709403,true +2018,10,21,1179040127,719140790,false +2018,10,21,358130767,403722447,true +2018,10,21,825161324,2085112676,true +2018,10,21,1449856582,1396007111,false +2018,10,21,1436757981,1143423061,true +2018,10,21,1504117571,1096481150,false +2018,10,21,185668047,200986727,false +2018,10,21,1713334948,1717352575,true +2018,10,21,1540549478,593361583,false +2018,10,21,1958824027,2086991569,false +2018,10,22,2137414992,727816404,false +2018,10,22,1170046044,1801686698,false +2018,10,22,2108708884,1261064349,true +2018,10,22,1369728446,1981441690,true +2018,10,22,1204146936,2120075280,false +2018,10,22,33379256,136969827,false +2018,10,22,1881546384,2009501612,false +2018,10,22,692854215,548607999,true +2018,10,22,1385651723,1099848108,true +2018,10,22,1604202907,638616452,true +2018,10,23,279320796,537337428,true +2018,10,23,1826205856,2046074595,true +2018,10,23,1634732628,203328696,false +2018,10,23,1539450942,548688565,false +2018,10,23,1616504642,6679672,true +2018,10,23,506058528,222284692,true +2018,10,23,935645622,1000550503,false +2018,10,23,2033229609,572912565,true +2018,10,23,1158623612,1052228382,true +2018,10,23,545031906,385897142,true +2018,10,24,1757787119,1238699492,false +2018,10,24,342775413,123374055,true +2018,10,24,1292099621,1531418480,true +2018,10,24,2114468774,1629204848,true +2018,10,24,373490862,1935797108,true +2018,10,24,1410778433,594310944,false +2018,10,24,1195694688,841360627,false +2018,10,24,283492844,388930005,false +2018,10,24,2090539993,1184163021,true +2018,10,24,299626354,1608533168,false +2018,10,25,93066183,80188166,false +2018,10,25,1592835791,637957013,false +2018,10,25,186045464,762975413,true +2018,10,25,843517614,30342831,true +2018,10,25,246478486,2077514142,true +2018,10,25,1984196895,965861,false +2018,10,25,51348390,312527839,true +2018,10,25,285584027,1775201673,false +2018,10,25,1980790297,43391296,false +2018,10,25,1545824195,1755338667,true +2018,10,26,1425627993,1459278083,false +2018,10,26,1283193941,83614157,false +2018,10,26,1292916051,1542142528,true +2018,10,26,261892429,1317211887,false +2018,10,26,1194940399,1808688700,true +2018,10,26,280934702,314721842,false +2018,10,26,474705393,191440696,true +2018,10,26,492570847,717217919,false +2018,10,26,566636955,981733286,false +2018,10,26,1597743274,59329515,true +2018,10,27,843776878,797851340,true +2018,10,27,1746707518,1079826145,true +2018,10,27,1362621904,1183340344,false +2018,10,27,1752787386,1507591469,false +2018,10,27,439578247,1524226204,false +2018,10,27,841991988,1006731134,false +2018,10,27,240793421,281348576,true +2018,10,27,800385848,1627632126,true +2018,10,27,1035513058,1553070474,true +2018,10,27,1981853739,1544898828,false +2018,10,28,1523646569,563915229,true +2018,10,28,133864355,586548032,false +2018,10,28,659136762,1579914030,false +2018,10,28,124842503,317150780,true +2018,10,28,36238241,1662390881,false +2018,10,28,1324401692,579549181,true +2018,10,28,1810304590,989031063,true +2018,10,28,1449291588,555323557,false +2018,10,28,654263904,1111853159,true +2018,10,28,1041760928,4684021,true +2018,10,29,1674419658,469434947,true +2018,10,29,331819601,1730086897,true +2018,10,29,1074692622,1111209170,false +2018,10,29,2132080520,1172774502,false +2018,10,29,1043887734,497951585,true +2018,10,29,422766381,492824903,true +2018,10,29,1058617180,488260551,false +2018,10,29,2111781439,965594139,false +2018,10,29,1800156766,638771782,true +2018,10,29,669637183,197368568,false +2018,10,30,237244366,1453401420,true +2018,10,30,1941369154,1395953592,true +2018,10,30,1609586625,97466925,false +2018,10,30,1120067706,255576173,false +2018,10,30,745859465,1339468578,true +2018,10,30,1791207773,1878748484,false +2018,10,30,1983860350,165977140,false +2018,10,30,371915954,1588685393,false +2018,10,30,916253023,1251475424,true +2018,10,30,863782948,1167525368,true +2018,10,31,176417211,736300435,false +2018,10,31,1120605753,1017583354,false +2018,10,31,833597208,656847697,false +2018,10,31,2108765616,779306311,false +2018,10,31,1689568727,291680543,false +2018,10,31,608187582,272770846,false +2018,10,31,1542592516,40589327,true +2018,10,31,646388124,1670397998,true +2018,10,31,1095031443,1696323583,false +2018,10,31,1126282598,1131838132,true +2018,11,1,1258240958,988048992,true +2018,11,1,1445054271,2111222462,true +2018,11,1,775057788,993719725,false +2018,11,1,1903703340,218556967,true +2018,11,1,719490883,22288465,false +2018,11,1,1517040940,1944438759,false +2018,11,1,180097180,2060391216,false +2018,11,1,1074474102,1479454658,false +2018,11,1,1496472115,846615313,false +2018,11,1,1133747175,896276359,false +2018,11,2,1833772385,514760088,true +2018,11,2,717912057,1901483352,false +2018,11,2,1561193062,1033679249,false +2018,11,2,967907305,1748633267,true +2018,11,2,1951038413,1411886010,true +2018,11,2,1750354639,863040052,true +2018,11,2,1330065843,1111217241,true +2018,11,2,1157524182,933492293,false +2018,11,2,262720992,1954944814,true +2018,11,2,225396183,616875329,false +2018,11,3,1060092412,25303724,true +2018,11,3,1645197309,448719783,false +2018,11,3,1793455939,1395328134,false +2018,11,3,1183035468,1345615737,false +2018,11,3,113056638,1781942496,true +2018,11,3,623204369,465739742,false +2018,11,3,492280118,1004451536,true +2018,11,3,1475218857,165450387,true +2018,11,3,104948056,1235230712,false +2018,11,3,1778262055,413671642,true +2018,11,4,1242053308,2137967183,false +2018,11,4,800099516,390264808,true +2018,11,4,50858073,1834754691,false +2018,11,4,2078521328,1832153341,true +2018,11,4,214839557,1724920437,true +2018,11,4,1624247305,346842023,true +2018,11,4,789087247,1559691961,true +2018,11,4,1127734072,658690282,true +2018,11,4,1881529372,169711397,false +2018,11,4,1741467490,648955962,false +2018,11,5,1758164493,1615099347,false +2018,11,5,902996639,697474959,false +2018,11,5,853738450,673705255,true +2018,11,5,426066628,419532280,false +2018,11,5,1809318780,500721835,true +2018,11,5,2101817238,1037630582,true +2018,11,5,776167659,1613260267,true +2018,11,5,1450174068,238355212,false +2018,11,5,1103295717,219730325,true +2018,11,5,1424316317,2124322888,true +2018,11,6,277782151,1622676099,false +2018,11,6,1246347355,929335206,false +2018,11,6,1149865021,1313446781,false +2018,11,6,2018257595,314990498,false +2018,11,6,108945362,1623381467,true +2018,11,6,175287966,466371168,true +2018,11,6,1384004914,1106994754,true +2018,11,6,1708814654,2042227741,false +2018,11,6,174265840,1207468603,false +2018,11,6,1452881794,2116905203,true +2018,11,7,83211190,900274258,true +2018,11,7,494894261,375711641,true +2018,11,7,1671475747,1736982015,false +2018,11,7,635843199,767846683,true +2018,11,7,1263981769,1850703239,false +2018,11,7,664791083,1654705609,false +2018,11,7,2012250175,1089964949,true +2018,11,7,1241441954,440827336,false +2018,11,7,1718658443,1084583745,false +2018,11,7,1882137509,1253488644,false +2018,11,8,1108587856,719818935,false +2018,11,8,523737640,1916730502,true +2018,11,8,2051359873,982663620,false +2018,11,8,453502687,1533878091,true +2018,11,8,1938587274,1507358955,false +2018,11,8,1695534937,1366484783,true +2018,11,8,152133928,1473475284,false +2018,11,8,1060300610,1391706262,false +2018,11,8,684301425,2025066296,false +2018,11,8,260876962,99323343,false +2018,11,9,1467735480,95540623,false +2018,11,9,577531177,405278564,false +2018,11,9,1697830473,962456362,true +2018,11,9,1117601270,1982811352,true +2018,11,9,1372161850,1148303243,true +2018,11,9,1813423190,1516298882,true +2018,11,9,1324536257,469290249,false +2018,11,9,161985895,104969095,true +2018,11,9,1391267789,1779481392,false +2018,11,9,35636143,275793691,true +2018,11,10,1836391131,993920342,false +2018,11,10,267231488,327327382,true +2018,11,10,445888309,237462359,false +2018,11,10,578316349,236365313,false +2018,11,10,1637181471,812450694,true +2018,11,10,803264485,1705906980,false +2018,11,10,757288357,463703076,true +2018,11,10,1942470503,1979361083,false +2018,11,10,397371137,826173443,true +2018,11,10,688500910,894756321,true +2018,11,11,1379826894,1204571425,true +2018,11,11,2002279531,1503494096,true +2018,11,11,173841551,1495741792,true +2018,11,11,1929546497,577272446,true +2018,11,11,1645249321,399966365,false +2018,11,11,402773278,2062783954,true +2018,11,11,351807071,743832795,false +2018,11,11,965469806,1881286484,true +2018,11,11,832163201,1149791299,false +2018,11,11,1029210597,1652926125,true +2018,11,12,523659192,963453465,true +2018,11,12,253198992,1793657172,false +2018,11,12,1086003907,1059866366,false +2018,11,12,1659763956,193387442,true +2018,11,12,864087808,1448251933,false +2018,11,12,1235240033,555701295,false +2018,11,12,202184202,702060556,false +2018,11,12,1747598533,1667266783,true +2018,11,12,1083514726,801016201,true +2018,11,12,2121784541,1026714310,false +2018,11,13,921604839,129608545,false +2018,11,13,696149467,236196337,false +2018,11,13,1348365009,1103119898,false +2018,11,13,1579314395,1311198164,false +2018,11,13,2044450798,1179031359,true +2018,11,13,514305977,46231981,true +2018,11,13,577447944,746110814,true +2018,11,13,916693544,677846747,false +2018,11,13,475374470,117280531,false +2018,11,13,917109021,97886059,false +2018,11,14,602755362,722518712,true +2018,11,14,168298982,319155532,true +2018,11,14,715052785,2145550226,false +2018,11,14,408348546,388804323,false +2018,11,14,1245099569,246484630,false +2018,11,14,364418083,174052346,false +2018,11,14,1688466330,414298132,false +2018,11,14,1222618582,335562300,false +2018,11,14,55210027,1933592816,true +2018,11,14,2122679485,670513345,false +2018,11,15,381146783,281765169,false +2018,11,15,408213111,1964574291,false +2018,11,15,850632729,992210181,true +2018,11,15,1922125174,1832439923,false +2018,11,15,347430560,1737277594,true +2018,11,15,156806260,1268859209,true +2018,11,15,60016259,561417298,false +2018,11,15,1318784339,1131446531,true +2018,11,15,113633222,592796218,true +2018,11,15,1155932162,1751886672,false +2018,11,16,67714843,576126676,true +2018,11,16,1155248442,185363445,true +2018,11,16,583584278,337661581,false +2018,11,16,2015838780,482406839,true +2018,11,16,1441311912,1986092670,false +2018,11,16,972550604,1737075974,false +2018,11,16,6309368,849002048,true +2018,11,16,1076605983,1460888627,true +2018,11,16,1703567670,608954877,false +2018,11,16,1437903487,2018547991,true +2018,11,17,238848771,625015563,true +2018,11,17,1763747014,1133517936,true +2018,11,17,895422076,289876651,true +2018,11,17,1784825344,295471456,false +2018,11,17,396215564,1602674123,false +2018,11,17,1136212706,1051283416,true +2018,11,17,1549617806,828504530,false +2018,11,17,1553415113,126713554,false +2018,11,17,2003983214,1420231139,true +2018,11,17,117409149,681949091,false +2018,11,18,1516483780,1410285859,true +2018,11,18,504421118,1089481165,true +2018,11,18,1831784091,765977045,false +2018,11,18,369840112,1262304031,true +2018,11,18,815908586,826577525,false +2018,11,18,1470946956,593699332,true +2018,11,18,1421926372,699732252,false +2018,11,18,1667069218,2006140994,false +2018,11,18,423337642,415101634,true +2018,11,18,1084589700,1602640557,false +2018,11,19,1091563397,1448480588,true +2018,11,19,2115735043,699631214,true +2018,11,19,1894468406,436499551,false +2018,11,19,1372402357,504635116,true +2018,11,19,228113653,731082861,true +2018,11,19,1667307336,1633013636,true +2018,11,19,463235898,1747090556,true +2018,11,19,1941289610,1180770946,false +2018,11,19,2048587116,335435412,true +2018,11,19,1586522728,1952095019,true +2018,11,20,1646601985,985255900,false +2018,11,20,599457315,1342927538,true +2018,11,20,1229143221,1124956356,false +2018,11,20,1514087895,141117538,true +2018,11,20,126997222,917816022,true +2018,11,20,2131717302,1557549941,true +2018,11,20,1392286774,1922970372,true +2018,11,20,1598480514,748548421,false +2018,11,20,2016855550,239937676,false +2018,11,20,292322344,1360221407,false +2018,11,21,450921533,736058305,false +2018,11,21,1542210700,1954854172,false +2018,11,21,1613115652,971038058,true +2018,11,21,271076839,818359504,false +2018,11,21,89443213,1438571111,true +2018,11,21,20180295,553428138,true +2018,11,21,1509818757,1950681313,false +2018,11,21,1869413643,573776095,false +2018,11,21,1753962793,341982010,true +2018,11,21,1134404725,608484532,false +2018,11,22,2122587468,759067237,false +2018,11,22,392516013,1540492419,true +2018,11,22,1275552864,2138946683,true +2018,11,22,635165377,1655282384,true +2018,11,22,1903526417,1857455525,true +2018,11,22,1067832711,1080708996,true +2018,11,22,2009626394,1103401144,false +2018,11,22,318142328,1665849456,true +2018,11,22,747050333,1550967306,true +2018,11,22,1898481101,399782049,false +2018,11,23,1845730914,2003849843,true +2018,11,23,2027772812,175847471,false +2018,11,23,522006721,2109116787,false +2018,11,23,1880923723,1388216451,true +2018,11,23,1839691060,1044178166,true +2018,11,23,805319627,1939280218,false +2018,11,23,737228344,2075476527,false +2018,11,23,1607678400,189892835,false +2018,11,23,856159624,997277302,true +2018,11,23,1196324444,17868226,true +2018,11,24,1442494681,1642733627,true +2018,11,24,427082618,2018677751,false +2018,11,24,1295727518,1038407527,true +2018,11,24,558606203,1495420354,true +2018,11,24,1337722305,1179068861,false +2018,11,24,724203058,345544738,true +2018,11,24,740861837,665107231,false +2018,11,24,636043638,1246930572,false +2018,11,24,734533952,2031295169,true +2018,11,24,1178756138,628937692,true +2018,11,25,324012295,103445935,false +2018,11,25,953736167,788475576,false +2018,11,25,1060785219,2134723635,false +2018,11,25,13354117,817391070,true +2018,11,25,438223215,2118277885,true +2018,11,25,1413901875,2025192719,true +2018,11,25,1193690626,146285085,false +2018,11,25,1870430695,17968531,false +2018,11,25,2055538718,990241574,false +2018,11,25,665045316,1569160596,true +2018,11,26,83951083,1316545826,true +2018,11,26,1889551950,161382332,true +2018,11,26,1940241649,1794250771,false +2018,11,26,279159939,1759862996,true +2018,11,26,199875022,608510778,false +2018,11,26,1973190703,1297906530,false +2018,11,26,925857047,73486426,false +2018,11,26,888396312,1364994416,false +2018,11,26,768422986,146469807,true +2018,11,26,394009812,1022561026,false +2018,11,27,510504473,1221243736,false +2018,11,27,217296486,985203688,true +2018,11,27,1943525239,1833089625,false +2018,11,27,1899557878,443670003,false +2018,11,27,380457770,90260108,false +2018,11,27,1305231041,1933431979,false +2018,11,27,1977432021,1058424342,false +2018,11,27,962076822,193521179,true +2018,11,27,1432879282,1430217782,false +2018,11,27,1798730611,681683036,false +2018,11,28,677935989,897738381,true +2018,11,28,1093255969,1239623123,true +2018,11,28,1645064725,511388464,true +2018,11,28,1377505518,419689062,true +2018,11,28,1433573734,2000508195,false +2018,11,28,869525701,1467472407,false +2018,11,28,2004141513,1435162636,false +2018,11,28,341230675,1659717058,false +2018,11,28,811998563,207457598,true +2018,11,28,1065587725,1308052722,false +2018,11,29,808747928,1787318128,false +2018,11,29,1854228259,2016965640,true +2018,11,29,661871547,728362938,true +2018,11,29,1357783748,624352297,true +2018,11,29,297177394,124092846,true +2018,11,29,1644951108,1888173749,false +2018,11,29,1518215552,2036682152,false +2018,11,29,33066248,149553191,false +2018,11,29,2011169014,1447231399,false +2018,11,29,1928165432,1260259003,true +2018,11,30,660614388,635221582,false +2018,11,30,421607852,978083626,false +2018,11,30,1762846466,18168034,false +2018,11,30,1904037529,1261839951,true +2018,11,30,2082077232,404825180,false +2018,11,30,2104255444,1122187435,true +2018,11,30,775266425,417079016,true +2018,11,30,1863544536,1495720175,false +2018,11,30,504737815,2033346155,false +2018,11,30,1397902796,1919595795,false +2018,11,31,245709076,1747200483,false +2018,11,31,161824311,2032735666,false +2018,11,31,571881851,1908039356,false +2018,11,31,1338621225,1469965724,true +2018,11,31,314350316,865532535,false +2018,11,31,1877642500,1814175701,true +2018,11,31,1192154765,1007482415,false +2018,11,31,1749056547,2047770757,true +2018,11,31,724202631,558699744,true +2018,11,31,1303538967,1254527362,true +2019,1,1,1865265803,1387421313,false +2019,1,1,1449581618,2129896826,true +2019,1,1,1978401057,1972990285,false +2019,1,1,25493391,1806021859,false +2019,1,1,1109281189,1271824330,false +2019,1,1,1583511015,1274361410,true +2019,1,1,631971644,1332403579,true +2019,1,1,2028246473,517406567,true +2019,1,1,439266095,1946266935,true +2019,1,1,310878733,1841532843,true +2019,1,2,1269981546,1348365206,true +2019,1,2,447857057,1618666324,true +2019,1,2,1205134350,795081988,false +2019,1,2,442525696,352431795,false +2019,1,2,434767194,578382413,true +2019,1,2,514011373,608480142,false +2019,1,2,1280318813,540925885,false +2019,1,2,818601293,2129268216,false +2019,1,2,933035298,276341300,true +2019,1,2,1353412662,34819495,true +2019,1,3,1359690591,1079890614,true +2019,1,3,2140680843,6604642,true +2019,1,3,995981004,452318302,true +2019,1,3,1590423737,2088459815,false +2019,1,3,104663210,111430087,false +2019,1,3,1402005881,507495951,false +2019,1,3,1954844196,1301526075,true +2019,1,3,578153887,676107037,false +2019,1,3,1950628240,97791053,false +2019,1,3,1935274073,1679823999,false +2019,1,4,1102648512,19654320,true +2019,1,4,729222264,1708228839,false +2019,1,4,816567249,791118174,false +2019,1,4,61447820,1372951297,false +2019,1,4,98933840,467769461,false +2019,1,4,1422447870,201882942,false +2019,1,4,1709098624,701835084,false +2019,1,4,617270895,1412997726,true +2019,1,4,1414410024,1665068467,true +2019,1,4,581917704,832623749,true +2019,1,5,328711732,2078126685,true +2019,1,5,1708626038,2011401924,true +2019,1,5,88761099,945963230,false +2019,1,5,950320588,694140243,true +2019,1,5,1246527856,660262601,true +2019,1,5,1385968518,1227867893,true +2019,1,5,432583226,2012993232,true +2019,1,5,467419993,1039447614,true +2019,1,5,494035284,1433256436,false +2019,1,5,116825752,572599216,true +2019,1,6,443303613,280213031,true +2019,1,6,1341214548,1450523114,true +2019,1,6,1673759365,1007085293,true +2019,1,6,386217867,1150813024,false +2019,1,6,437167090,1203937248,false +2019,1,6,1574661619,1995699539,true +2019,1,6,702759513,156844583,true +2019,1,6,1003737929,1635540484,false +2019,1,6,1089657428,643327285,true +2019,1,6,348267575,976219223,false +2019,1,7,388062280,1310886006,true +2019,1,7,162294559,886830431,true +2019,1,7,4731276,2014647988,false +2019,1,7,547438,141310088,false +2019,1,7,1441493903,37827917,false +2019,1,7,889492310,1466291553,true +2019,1,7,1794536638,1688324256,true +2019,1,7,824212969,1046564400,false +2019,1,7,1307882790,1419742262,true +2019,1,7,1181740821,1200445652,false +2019,1,8,55987469,161015949,false +2019,1,8,1034925562,1680143269,false +2019,1,8,1703101925,1535353235,true +2019,1,8,38477179,2083014219,false +2019,1,8,1403803248,263020599,true +2019,1,8,913819697,1629378255,true +2019,1,8,1575260402,1679410836,false +2019,1,8,523885560,1607604552,false +2019,1,8,1262344890,1134231971,true +2019,1,8,11306967,366996726,false +2019,1,9,1292372590,1657795705,true +2019,1,9,1414945261,1006683053,false +2019,1,9,1910660836,2049046649,false +2019,1,9,1560472746,1766918354,false +2019,1,9,1474966113,1360401161,true +2019,1,9,1850669445,80048997,false +2019,1,9,307064477,1801600160,false +2019,1,9,489499867,170742477,false +2019,1,9,386917346,1670395473,true +2019,1,9,1458239954,1458448468,false +2019,1,10,1848660500,781926939,false +2019,1,10,81294918,2059964315,false +2019,1,10,1947483275,1560096674,false +2019,1,10,633718315,1373841020,false +2019,1,10,17013253,1088381476,true +2019,1,10,635314929,1903780093,false +2019,1,10,494512156,423955910,false +2019,1,10,12838597,1366134896,false +2019,1,10,1292152322,821148039,true +2019,1,10,460555464,1447947474,false +2019,1,11,952687726,546334850,false +2019,1,11,1477693072,1104328190,true +2019,1,11,1543227652,227047489,true +2019,1,11,1694016662,1157207647,false +2019,1,11,1657721244,1597150033,false +2019,1,11,834142805,1050272536,true +2019,1,11,552800306,1791811161,true +2019,1,11,701993031,981677536,true +2019,1,11,2126316141,1540756318,true +2019,1,11,215962894,304659204,false +2019,1,12,191344625,1567449598,false +2019,1,12,1676874845,660568655,false +2019,1,12,1196261636,620491971,true +2019,1,12,1794323845,1046768582,true +2019,1,12,367668012,1043184354,false +2019,1,12,682425520,1519608180,true +2019,1,12,219589579,12311297,false +2019,1,12,1149454640,2096383434,false +2019,1,12,1272662245,1037291222,false +2019,1,12,13711466,1735848494,true +2019,1,13,1529908892,1493952704,false +2019,1,13,1481691211,886866901,false +2019,1,13,2050444285,400538535,false +2019,1,13,1223982402,939042049,false +2019,1,13,997211186,854383304,false +2019,1,13,334729015,456019757,false +2019,1,13,2020696385,401111609,true +2019,1,13,1565672953,1326851808,false +2019,1,13,772516816,1525150272,true +2019,1,13,1160067347,29117448,false +2019,1,14,377319349,855496417,false +2019,1,14,690964498,1558143547,true +2019,1,14,1370679088,345606694,false +2019,1,14,1106220953,1902197299,true +2019,1,14,1452193572,1901312775,true +2019,1,14,2038086870,621083212,false +2019,1,14,827430063,550269966,false +2019,1,14,1912925243,1504736772,true +2019,1,14,705030376,234488829,true +2019,1,14,1880758663,2095817887,true +2019,1,15,1073621431,221214751,false +2019,1,15,1169245822,1847485826,true +2019,1,15,466200311,129983258,false +2019,1,15,137892580,1556221153,false +2019,1,15,307908972,221221963,false +2019,1,15,341232535,1427185106,true +2019,1,15,1024496944,1490354971,false +2019,1,15,2136541150,1650929386,true +2019,1,15,1125274523,1144968911,true +2019,1,15,1988504388,1559781,true +2019,1,16,247228644,1604054715,true +2019,1,16,35401624,1531663117,true +2019,1,16,1451198431,2047406059,true +2019,1,16,331333100,1375843168,false +2019,1,16,789160615,1668576401,true +2019,1,16,1639065253,1176377503,true +2019,1,16,468984086,1342953186,false +2019,1,16,587270138,1609808588,true +2019,1,16,600301144,2041775146,true +2019,1,16,661567357,2085632710,false +2019,1,17,35970307,1090264628,true +2019,1,17,1555406455,1781396480,false +2019,1,17,1925899631,1246052455,true +2019,1,17,1102379585,1081080708,true +2019,1,17,733963333,344576795,true +2019,1,17,632400103,942705099,false +2019,1,17,1326247814,1985299224,true +2019,1,17,135407536,527655172,true +2019,1,17,700606072,12846148,true +2019,1,17,1523022949,1989800933,false +2019,1,18,1330560299,1318604250,false +2019,1,18,384613226,1784394020,true +2019,1,18,115261097,79580111,true +2019,1,18,1687227394,2075031064,false +2019,1,18,1965193446,2016502830,false +2019,1,18,1157284196,2038552387,true +2019,1,18,2051484672,293402761,false +2019,1,18,1617504859,1849374635,false +2019,1,18,930777733,1869056731,false +2019,1,18,1358654123,93036620,false +2019,1,19,1830709929,1707717225,false +2019,1,19,2005915769,2041937680,true +2019,1,19,1817036976,472033414,true +2019,1,19,339035485,835057923,true +2019,1,19,208255235,44807753,true +2019,1,19,1833967086,1448140238,false +2019,1,19,1608955049,1987134351,true +2019,1,19,423343077,447257232,true +2019,1,19,105911565,1477706982,true +2019,1,19,2046109229,344807600,true +2019,1,20,1615047779,1451530543,true +2019,1,20,498827126,2010894666,true +2019,1,20,884784653,522133786,false +2019,1,20,1739562793,2108741679,false +2019,1,20,634011335,1154992591,false +2019,1,20,1288151499,1836665285,false +2019,1,20,1299564163,1213938008,true +2019,1,20,1232835890,1845325086,true +2019,1,20,582487618,2044335787,false +2019,1,20,211067421,984420382,true +2019,1,21,2023604813,1275779177,true +2019,1,21,2011318284,1451242901,true +2019,1,21,510651795,968060072,false +2019,1,21,302129510,1855204742,true +2019,1,21,917101852,2141288806,true +2019,1,21,760919377,1665855008,false +2019,1,21,54665939,366327923,false +2019,1,21,754938324,2105184270,false +2019,1,21,1582556439,704158453,false +2019,1,21,1229902857,1946975016,false +2019,1,22,1121647814,340049883,true +2019,1,22,1711363472,1923754162,true +2019,1,22,1446647245,1446587848,false +2019,1,22,246070664,1351284385,false +2019,1,22,828057185,1091665007,true +2019,1,22,631521457,1947823016,false +2019,1,22,1962192737,661551699,false +2019,1,22,2123775880,2092699125,true +2019,1,22,528573596,850735963,false +2019,1,22,796375675,1873780458,true +2019,1,23,564897265,325900749,false +2019,1,23,315659143,1230945329,true +2019,1,23,1803397754,990103168,false +2019,1,23,1192016348,331981570,true +2019,1,23,786756935,912776810,true +2019,1,23,710489954,1112255599,false +2019,1,23,1010937685,93584083,false +2019,1,23,1308236073,1349132821,false +2019,1,23,1302226115,1082181335,false +2019,1,23,132129586,1131401348,true +2019,1,24,1454076783,654290937,true +2019,1,24,299859221,575426867,true +2019,1,24,771976,251615478,false +2019,1,24,421714023,751761386,true +2019,1,24,351952567,2138916128,true +2019,1,24,1036012275,388542430,true +2019,1,24,1304757478,722239451,false +2019,1,24,706938404,356676336,false +2019,1,24,1239410137,1817093919,true +2019,1,24,1906149934,18837977,false +2019,1,25,520975624,1525106951,false +2019,1,25,742833882,925503009,true +2019,1,25,2003362185,798606289,false +2019,1,25,1094634757,145522483,true +2019,1,25,375938527,2146049252,true +2019,1,25,1496415016,708047346,true +2019,1,25,557122576,179396271,false +2019,1,25,1054833803,2131048083,false +2019,1,25,1327799375,1318014593,false +2019,1,25,1891657202,1710895791,true +2019,1,26,1865816343,350519981,true +2019,1,26,322380766,1887985093,false +2019,1,26,1898694040,1295605391,false +2019,1,26,1514909120,1656429735,true +2019,1,26,1287715764,1842744662,true +2019,1,26,1766889127,1581051014,true +2019,1,26,913263879,370445724,false +2019,1,26,6412121,1680050327,false +2019,1,26,357985971,258281259,false +2019,1,26,1830620883,1081908621,true +2019,1,27,17332701,1463064974,false +2019,1,27,1155616160,1178649685,true +2019,1,27,1198670630,2106719545,false +2019,1,27,786800016,2048094682,true +2019,1,27,2059811200,358269539,true +2019,1,27,383756975,1249500094,true +2019,1,27,472086832,600567801,true +2019,1,27,787385227,1419088451,true +2019,1,27,1941314017,13988392,false +2019,1,27,762672690,1915112930,false +2019,1,28,1919960846,1408002924,false +2019,1,28,51217512,179387196,false +2019,1,28,762574893,1273766967,true +2019,1,28,394810629,171604590,true +2019,1,28,1305817702,220163191,false +2019,1,28,786213250,101883623,false +2019,1,28,1492529080,377972661,false +2019,1,28,307029521,265311065,true +2019,1,28,2043062228,625522557,false +2019,1,28,1037690852,1917577564,false +2019,1,29,1952601382,914233675,false +2019,1,29,196397156,2085428947,true +2019,1,29,1732295859,1466344507,true +2019,1,29,677588360,1868288826,true +2019,1,29,712559068,241578020,false +2019,1,29,1955890832,298538347,false +2019,1,29,1734412073,37610217,true +2019,1,29,1482512847,1126264431,false +2019,1,29,1569960870,1228454439,false +2019,1,29,1698718534,964814975,true +2019,1,30,1157806198,1971385998,true +2019,1,30,1806501185,1401690436,false +2019,1,30,961553578,1321591670,false +2019,1,30,1743668019,106986729,true +2019,1,30,1637705086,770269347,false +2019,1,30,1623030918,1239597996,true +2019,1,30,1610514317,387917096,false +2019,1,30,194392090,440559080,false +2019,1,30,1031481749,1939323371,true +2019,1,30,1331923620,72741582,true +2019,1,31,2015978468,1090713411,false +2019,1,31,965882163,1949330509,true +2019,1,31,1123749770,1321940114,false +2019,1,31,1840705147,1551118033,true +2019,1,31,991997661,1815519740,true +2019,1,31,1830909123,241937895,false +2019,1,31,1044442358,249054085,true +2019,1,31,4626303,1632159463,false +2019,1,31,1941986350,1675102770,true +2019,1,31,527566588,1940529456,false +2019,2,1,1085021127,1132028689,true +2019,2,1,781256116,2032736157,true +2019,2,1,752964415,155882792,false +2019,2,1,2049937943,1749518649,false +2019,2,1,1475526152,442777279,true +2019,2,1,455829743,599572321,true +2019,2,1,1027894419,901218179,false +2019,2,1,655156144,242476499,false +2019,2,1,906983411,1605939311,true +2019,2,1,1103059787,1726049621,false +2019,2,2,1402354557,1365984132,false +2019,2,2,294728546,937077759,true +2019,2,2,1334561849,816207028,false +2019,2,2,121656042,451622483,false +2019,2,2,346318848,753330823,false +2019,2,2,1524721315,1545360024,false +2019,2,2,1666811536,1237889135,true +2019,2,2,1101997806,44997697,true +2019,2,2,138650887,310732220,false +2019,2,2,42520034,1845362496,true +2019,2,3,279044223,1064903599,false +2019,2,3,1202930616,1140437911,true +2019,2,3,1123520198,140134949,true +2019,2,3,1286346463,1504272264,true +2019,2,3,2112965734,452011463,true +2019,2,3,1954441823,1004933934,false +2019,2,3,191626608,598772687,true +2019,2,3,1523871819,821623396,true +2019,2,3,2016613073,80643761,false +2019,2,3,1964578675,1241348683,false +2019,2,4,1333368859,294458453,true +2019,2,4,1642076832,400262606,false +2019,2,4,1497902590,1235136931,false +2019,2,4,1083895027,1808226539,false +2019,2,4,84259824,1561481362,true +2019,2,4,1122034242,378735800,true +2019,2,4,1676218285,281357552,true +2019,2,4,1077132172,1916642897,false +2019,2,4,198167447,1826034086,true +2019,2,4,1728593623,305852728,true +2019,2,5,791821607,1050465058,false +2019,2,5,1971783445,974893519,true +2019,2,5,376249058,1853119788,true +2019,2,5,762581988,1506940655,false +2019,2,5,1039658386,1499103573,false +2019,2,5,1224024648,1228528081,false +2019,2,5,1586309817,1392183481,true +2019,2,5,234770089,1329906107,true +2019,2,5,654644852,1245394646,false +2019,2,5,490880450,477451568,true +2019,2,6,325605702,1391148184,true +2019,2,6,459070782,484177054,true +2019,2,6,1644073605,1529274876,false +2019,2,6,903677501,222001845,true +2019,2,6,892054113,65878469,true +2019,2,6,304528111,1347312877,false +2019,2,6,721637050,2037143109,true +2019,2,6,1746120204,838087403,true +2019,2,6,1704160318,288096040,true +2019,2,6,653659605,1504777661,true +2019,2,7,864987534,701638581,true +2019,2,7,1359969751,1400340522,true +2019,2,7,552631381,1880074772,false +2019,2,7,1293849013,1442107274,true +2019,2,7,1675260051,237725548,true +2019,2,7,1504987289,1236682977,true +2019,2,7,600455314,560901631,false +2019,2,7,1988807710,1320560682,false +2019,2,7,323102778,841907591,false +2019,2,7,29515479,1023856675,false +2019,2,8,1416718527,2053259139,false +2019,2,8,227132862,338575090,true +2019,2,8,1379502560,431463481,true +2019,2,8,739695907,1180272757,true +2019,2,8,90614831,855346836,true +2019,2,8,401229060,1984090407,false +2019,2,8,193225753,1193773098,false +2019,2,8,1105162705,1094397143,true +2019,2,8,28391339,1399106564,false +2019,2,8,15713788,525560488,false +2019,2,9,1992706369,1785901583,false +2019,2,9,1632368120,1295338513,false +2019,2,9,258535330,616642101,false +2019,2,9,582527847,1923839705,false +2019,2,9,309639232,1643371914,false +2019,2,9,821654955,454472256,true +2019,2,9,2064198284,1101539613,true +2019,2,9,2027185434,565657043,true +2019,2,9,1224125341,712036676,true +2019,2,9,994018169,1144085665,true +2019,2,10,668968863,1435594111,true +2019,2,10,1380681625,1249647050,false +2019,2,10,445168326,1161219145,true +2019,2,10,519172899,1206641565,false +2019,2,10,1934984630,978256744,true +2019,2,10,1115361524,1201828030,true +2019,2,10,1704278430,789036195,true +2019,2,10,1162762141,224429845,true +2019,2,10,572933758,2044912616,false +2019,2,10,320820961,1524252262,false +2019,2,11,1607729736,1912632123,false +2019,2,11,1508761672,1030506735,false +2019,2,11,1276848611,1895298579,true +2019,2,11,805460507,1170815578,false +2019,2,11,1576391988,350965997,false +2019,2,11,1223782766,868532705,false +2019,2,11,1422880186,1714795319,true +2019,2,11,1127512827,138667648,false +2019,2,11,1813740391,349841641,false +2019,2,11,880219134,229350525,true +2019,2,12,178011568,64181855,true +2019,2,12,1355139924,1821446584,false +2019,2,12,1763776271,1470452818,true +2019,2,12,1811749001,1491264125,false +2019,2,12,1615945587,913879816,true +2019,2,12,534870420,1881250507,true +2019,2,12,807030343,1078801169,true +2019,2,12,434669948,400394321,false +2019,2,12,1326573343,501223203,false +2019,2,12,775164184,1622966162,true +2019,2,13,1752972327,511088122,false +2019,2,13,1412548767,655090407,true +2019,2,13,2076868587,1812831308,false +2019,2,13,250121168,803308643,true +2019,2,13,1285444624,1905380899,true +2019,2,13,545485775,617870267,false +2019,2,13,58791189,1452174409,true +2019,2,13,830959892,1347616705,true +2019,2,13,968074039,2079284609,false +2019,2,13,18672888,1484896031,false +2019,2,14,1668540171,88734917,false +2019,2,14,467566022,1083283012,false +2019,2,14,979508335,2069136580,true +2019,2,14,1506484567,1719918796,true +2019,2,14,1776310491,994489933,true +2019,2,14,504271272,1356526278,true +2019,2,14,804117051,1489840756,true +2019,2,14,445082605,131969704,true +2019,2,14,1654129117,848493196,true +2019,2,14,640736768,1747896016,true +2019,2,15,1808433940,163567437,false +2019,2,15,721792838,1108793818,true +2019,2,15,998559647,1284611523,true +2019,2,15,1257476458,37461828,true +2019,2,15,1183661047,904643544,true +2019,2,15,921698924,2053013004,true +2019,2,15,1763918865,644999052,true +2019,2,15,1465482493,1545558347,true +2019,2,15,1206768947,37237578,false +2019,2,15,956271911,340717312,true +2019,2,16,772545522,1306556112,true +2019,2,16,2125426338,400376706,true +2019,2,16,805707122,1549244321,false +2019,2,16,449802103,1761844311,true +2019,2,16,723839354,255829254,true +2019,2,16,202194097,1653487456,true +2019,2,16,2055898885,142097149,false +2019,2,16,1802489939,1078150045,true +2019,2,16,1815703369,338904238,true +2019,2,16,2107096781,131938027,true +2019,2,17,1267698548,1976527421,true +2019,2,17,648310691,2040299672,true +2019,2,17,1859504016,306501390,false +2019,2,17,1718890498,178084508,false +2019,2,17,148076870,1912298715,true +2019,2,17,1033590651,1354443730,false +2019,2,17,1669047167,85628203,true +2019,2,17,70563357,1449925520,true +2019,2,17,1286903822,1665357320,false +2019,2,17,386238618,489763685,false +2019,2,18,1811623662,560785418,true +2019,2,18,1119423051,1973765616,true +2019,2,18,1731710647,1743043339,false +2019,2,18,2064956963,1152244230,true +2019,2,18,823288911,886254632,false +2019,2,18,88964853,1101799330,true +2019,2,18,2014066705,2048858839,true +2019,2,18,1597416603,1701480533,false +2019,2,18,1975246036,1038736440,true +2019,2,18,1115545307,636981042,true +2019,2,19,1888244124,1724932844,true +2019,2,19,683089955,1202494564,false +2019,2,19,2080925917,125402908,false +2019,2,19,450421103,2024099579,true +2019,2,19,1218293907,476953969,true +2019,2,19,1140882935,383771526,false +2019,2,19,373095588,728167718,false +2019,2,19,386150516,1507489946,true +2019,2,19,456943933,859624590,true +2019,2,19,859417894,389917274,false +2019,2,20,595115308,1720909969,true +2019,2,20,675298008,103150740,false +2019,2,20,431461488,1663334159,false +2019,2,20,1908923027,1248890648,false +2019,2,20,1235040615,1084761402,true +2019,2,20,488001743,598972998,true +2019,2,20,493395814,947575350,true +2019,2,20,234490925,778928666,true +2019,2,20,1523915687,23804835,false +2019,2,20,866305328,782568462,true +2019,2,21,289362373,1294136545,false +2019,2,21,371365739,1410244782,false +2019,2,21,606330695,1672762154,false +2019,2,21,1090523156,848796601,false +2019,2,21,1561900521,525447133,false +2019,2,21,903581451,18667035,true +2019,2,21,404969114,738377606,false +2019,2,21,2064068269,779928364,false +2019,2,21,2126466932,414640010,false +2019,2,21,174621708,1637969649,false +2019,2,22,1706691860,1491627395,true +2019,2,22,2082782309,414514518,false +2019,2,22,233262105,952466096,true +2019,2,22,223950313,416352918,false +2019,2,22,443011512,1166924543,true +2019,2,22,1101627703,202574677,true +2019,2,22,867801765,414245536,false +2019,2,22,1509821975,1491441422,true +2019,2,22,1258550722,1569803813,false +2019,2,22,285241682,1330639335,false +2019,2,23,969741391,548278415,true +2019,2,23,1076396738,1158497035,false +2019,2,23,1219693461,482420712,false +2019,2,23,1087592940,264649492,true +2019,2,23,33891393,2035846191,true +2019,2,23,1683406019,1735430710,false +2019,2,23,1387278823,267021762,true +2019,2,23,1973598052,1111836699,true +2019,2,23,653367655,990646159,false +2019,2,23,1309351141,1232732092,true +2019,2,24,1015559979,225044142,false +2019,2,24,250376347,1773387674,true +2019,2,24,222006903,80616502,true +2019,2,24,133004378,2084428571,true +2019,2,24,735256895,1738499940,true +2019,2,24,535851924,1330275329,true +2019,2,24,413635649,1716143482,true +2019,2,24,95241336,73485102,true +2019,2,24,1955784023,269494741,true +2019,2,24,783380299,464444985,false +2019,2,25,1954384441,161950758,true +2019,2,25,682670520,2120983416,false +2019,2,25,409230378,2066631654,false +2019,2,25,574301907,1600145552,true +2019,2,25,1308283203,1809409869,true +2019,2,25,430904817,1883600814,true +2019,2,25,1860288091,698405233,true +2019,2,25,1337924019,1677559272,false +2019,2,25,169067710,299161192,false +2019,2,25,27282897,151853447,true +2019,2,26,1026831658,2143864184,false +2019,2,26,972782978,321892641,false +2019,2,26,1551214035,1607735479,false +2019,2,26,322346397,597191880,false +2019,2,26,1142704988,375215788,false +2019,2,26,417318081,598814916,true +2019,2,26,1454531840,455157206,false +2019,2,26,110836010,1077427594,true +2019,2,26,518998632,1519520542,true +2019,2,26,498937593,1698970353,false +2019,2,27,436467168,1067008926,true +2019,2,27,917027611,165250255,false +2019,2,27,1332334563,814704452,true +2019,2,27,466165270,296867211,false +2019,2,27,107288893,2110903615,true +2019,2,27,681258844,2008368965,true +2019,2,27,1778788142,995599408,false +2019,2,27,2055875842,660381346,true +2019,2,27,890882392,915062683,true +2019,2,27,2020005088,27261530,true +2019,2,28,2029990750,1891520780,true +2019,2,28,1362641798,2043734304,true +2019,2,28,359249015,557735653,false +2019,2,28,333181591,1609931649,true +2019,2,28,428799340,304282585,true +2019,2,28,744694621,1514535352,true +2019,2,28,1585672625,2136885627,false +2019,2,28,1622105011,1398269379,false +2019,2,28,1535769246,2053389673,false +2019,2,28,865741466,1299869708,true +2019,2,29,595549174,157768704,true +2019,2,29,2091679515,1110987145,false +2019,2,29,527908661,1111606426,true +2019,2,29,1351829308,301475870,true +2019,2,29,75841378,1605290686,false +2019,2,29,922689072,347589288,false +2019,2,29,921588057,1969306837,true +2019,2,29,1034835984,1775902714,false +2019,2,29,1013990596,735917115,false +2019,2,29,1156056767,16926894,false +2019,2,30,1453047874,1050880966,false +2019,2,30,1542289095,1642671521,true +2019,2,30,321963881,985395482,false +2019,2,30,2123459655,87340396,true +2019,2,30,1963860903,2041193053,true +2019,2,30,970057559,764055864,true +2019,2,30,618397998,1434721588,false +2019,2,30,827636844,374328772,true +2019,2,30,1167869804,1017634054,true +2019,2,30,1648404631,802008946,true +2019,2,31,2048243909,797316599,false +2019,2,31,920455867,1073408181,false +2019,2,31,1871660935,267464484,false +2019,2,31,1843454486,1176797243,true +2019,2,31,1384668743,1040411134,false +2019,2,31,191262625,623235236,true +2019,2,31,260155443,1673713108,true +2019,2,31,932293436,1161592231,true +2019,2,31,1538540028,632583666,true +2019,2,31,1949038246,1211177709,false +2019,3,1,668382809,1061588448,true +2019,3,1,1188816804,71573274,false +2019,3,1,666816870,820914849,true +2019,3,1,1763899374,1751689642,false +2019,3,1,1569438780,1367935234,false +2019,3,1,115881145,183184568,true +2019,3,1,1196885652,334739548,false +2019,3,1,1034680562,1955349443,false +2019,3,1,1018091108,1290706522,false +2019,3,1,918561935,47871759,false +2019,3,2,1367111077,2012421014,false +2019,3,2,1780904059,1428057911,true +2019,3,2,331183309,1930285618,true +2019,3,2,1706902020,41533188,false +2019,3,2,842704027,700106427,false +2019,3,2,798362080,393869738,true +2019,3,2,458150992,954994929,true +2019,3,2,2118097432,81844731,true +2019,3,2,1215994211,1822549326,false +2019,3,2,101008257,713218863,true +2019,3,3,1073544273,363477341,true +2019,3,3,1314440842,1443061323,false +2019,3,3,287885857,1571025990,true +2019,3,3,692749443,1902600023,true +2019,3,3,867765418,519819798,true +2019,3,3,457684403,449078241,true +2019,3,3,1834343361,1267968938,false +2019,3,3,227149261,1720893562,true +2019,3,3,1776150488,713885263,false +2019,3,3,1185721255,787096883,false +2019,3,4,2012011195,818443627,false +2019,3,4,1350576707,48931141,false +2019,3,4,1520862565,2030093774,false +2019,3,4,209991818,1730120808,false +2019,3,4,140106278,235203970,true +2019,3,4,1032659522,703926405,false +2019,3,4,929790101,162636839,true +2019,3,4,1283137584,2006186182,true +2019,3,4,4728064,1053566794,true +2019,3,4,1810101853,886375219,true +2019,3,5,2104850261,1293767477,true +2019,3,5,156004089,949471324,true +2019,3,5,498560445,841921568,false +2019,3,5,929322326,40333504,true +2019,3,5,1099492705,169596121,false +2019,3,5,1792423915,906916336,false +2019,3,5,1850451982,1904715577,true +2019,3,5,147308895,74677666,true +2019,3,5,1590749108,385400823,true +2019,3,5,883472400,620051243,true +2019,3,6,120289523,1978471108,true +2019,3,6,1613232274,1460953119,false +2019,3,6,1891492917,108410030,true +2019,3,6,541514515,1339021059,true +2019,3,6,339568900,1719581248,false +2019,3,6,146650287,1921248089,true +2019,3,6,1023889823,955145175,false +2019,3,6,828267798,1607104618,false +2019,3,6,475809020,444659232,true +2019,3,6,223092245,1316547739,false +2019,3,7,1367657394,1584500161,true +2019,3,7,2015954390,1091249461,false +2019,3,7,1550801031,1133577373,true +2019,3,7,356421123,1537525475,true +2019,3,7,88498072,1998879787,true +2019,3,7,1412413699,759321935,true +2019,3,7,1668509517,1400587227,false +2019,3,7,38501391,2042624176,false +2019,3,7,2104583322,914553730,true +2019,3,7,1829459929,609036831,false +2019,3,8,464373823,1603473727,false +2019,3,8,280539879,558200845,false +2019,3,8,288470276,1808353116,false +2019,3,8,12496899,266979895,false +2019,3,8,1270507298,102713872,false +2019,3,8,409086024,1572409567,true +2019,3,8,2093306710,551476789,true +2019,3,8,1428157361,1364766015,true +2019,3,8,1663791193,1119725901,true +2019,3,8,1839600700,576532843,false +2019,3,9,1586153388,150163561,true +2019,3,9,79656696,646702112,false +2019,3,9,1413446391,2043313870,false +2019,3,9,1989551001,366006571,true +2019,3,9,1648935280,2077138545,false +2019,3,9,1024241258,1551346036,true +2019,3,9,1964049669,438401950,true +2019,3,9,1569120694,1936612819,true +2019,3,9,987744574,434373218,false +2019,3,9,74033910,494257710,false +2019,3,10,952727057,221220760,false +2019,3,10,93042031,1236327070,true +2019,3,10,1501368088,717176995,false +2019,3,10,816353026,1505203846,true +2019,3,10,1125538807,1217205312,false +2019,3,10,182390108,228367838,true +2019,3,10,2138546380,1252645871,false +2019,3,10,1112914119,481868584,false +2019,3,10,1335873012,1869791460,true +2019,3,10,1206097741,40720580,false +2019,3,11,1837077666,1621543051,true +2019,3,11,74751469,2043069085,true +2019,3,11,2060132841,1184048116,false +2019,3,11,177512201,1289442668,true +2019,3,11,871429193,174095048,true +2019,3,11,1010078823,994072314,true +2019,3,11,720199660,1064672670,true +2019,3,11,1965092937,409343788,true +2019,3,11,1966240772,2073980092,false +2019,3,11,1037960336,1617476551,false +2019,3,12,393551893,43980718,false +2019,3,12,1649203183,226150686,true +2019,3,12,1078608401,562853693,false +2019,3,12,1024442503,886220850,false +2019,3,12,1355005575,1323300516,true +2019,3,12,576004236,2000758508,false +2019,3,12,1333885530,1926208030,false +2019,3,12,999783624,1991756149,false +2019,3,12,1034555837,1766195223,false +2019,3,12,1789273617,161074140,false +2019,3,13,1095305824,324079534,false +2019,3,13,620733964,1867544965,false +2019,3,13,1963265815,431239754,false +2019,3,13,1762614484,1369773084,true +2019,3,13,1893273956,286377837,false +2019,3,13,983044194,1974040061,false +2019,3,13,1225460133,759286029,false +2019,3,13,1970071494,1061289039,false +2019,3,13,343279831,1434297361,false +2019,3,13,1032207246,1103864649,false +2019,3,14,280655666,1316809686,false +2019,3,14,1515067891,311995224,false +2019,3,14,824525060,668407994,true +2019,3,14,1200433314,2005674007,true +2019,3,14,1892116373,1304740127,false +2019,3,14,2032729915,674533990,false +2019,3,14,1260682749,414253425,false +2019,3,14,16514268,1850305787,true +2019,3,14,2057116212,107078403,true +2019,3,14,544229456,1722699584,false +2019,3,15,1341046979,256234215,true +2019,3,15,399371711,158583906,false +2019,3,15,1003314649,957575444,false +2019,3,15,346174960,1689107667,false +2019,3,15,1715112121,1058045291,false +2019,3,15,1119695477,1430716089,false +2019,3,15,1154983610,129121151,true +2019,3,15,6578019,459333114,false +2019,3,15,1114380619,698130221,false +2019,3,15,673052260,2009829086,true +2019,3,16,701368003,1083360099,false +2019,3,16,104427576,715427469,true +2019,3,16,112234459,1028710834,true +2019,3,16,621119758,408281799,true +2019,3,16,474379302,548539503,false +2019,3,16,1871919420,1330128556,true +2019,3,16,362599917,960470679,false +2019,3,16,1523402543,1803723889,true +2019,3,16,2054883878,1656257467,true +2019,3,16,875868347,1397587021,false +2019,3,17,553499539,1997481866,true +2019,3,17,1084750097,661034764,true +2019,3,17,901525536,1404649448,false +2019,3,17,826588667,34270980,true +2019,3,17,2093270554,705449350,true +2019,3,17,570663475,1602888805,false +2019,3,17,1487131355,915144314,true +2019,3,17,475786229,1088776723,true +2019,3,17,1005027062,884409167,true +2019,3,17,1376845338,311897748,true +2019,3,18,2115050513,821150326,false +2019,3,18,1669095254,2134149299,true +2019,3,18,182275819,2010265390,false +2019,3,18,239412855,10088934,true +2019,3,18,1662538814,61171768,true +2019,3,18,1921297334,252397729,true +2019,3,18,1433022846,376782829,false +2019,3,18,641801850,1889272589,false +2019,3,18,1107483305,1430454935,true +2019,3,18,277570584,1666447084,false +2019,3,19,332582044,487487765,false +2019,3,19,1551777654,728937863,false +2019,3,19,427253680,1222178107,false +2019,3,19,1592427171,271467261,false +2019,3,19,850442376,1693706855,true +2019,3,19,1638777526,941127244,false +2019,3,19,1529531736,423149630,true +2019,3,19,1071055774,386573287,false +2019,3,19,1218708490,1850218137,false +2019,3,19,1512367263,2014504636,true +2019,3,20,391023124,586987778,true +2019,3,20,779243441,47610865,true +2019,3,20,575750358,1440632147,true +2019,3,20,305552647,276677576,false +2019,3,20,529596886,99822674,false +2019,3,20,495997275,267429474,true +2019,3,20,1337566267,2007702633,false +2019,3,20,691874005,311747935,true +2019,3,20,1144103252,745973181,false +2019,3,20,1614172591,701389708,false +2019,3,21,306513933,1198177369,false +2019,3,21,1285284058,1564860955,false +2019,3,21,1503768477,695097391,true +2019,3,21,643561555,1937020700,true +2019,3,21,1628166101,1539347464,true +2019,3,21,424614383,212607789,true +2019,3,21,867152348,226550737,true +2019,3,21,1291389627,175271073,true +2019,3,21,1297167927,459812794,true +2019,3,21,1562937935,480277639,true +2019,3,22,1854621035,1588524322,true +2019,3,22,1361561250,1545401946,false +2019,3,22,1273751203,1398884176,true +2019,3,22,1838936119,1425497416,true +2019,3,22,1548459035,206731606,true +2019,3,22,777843657,78498373,false +2019,3,22,1037485888,932032453,true +2019,3,22,1383462332,552062060,true +2019,3,22,290129603,295855087,true +2019,3,22,1990985972,2110857573,true +2019,3,23,1116202773,1212240874,true +2019,3,23,69242348,463332789,true +2019,3,23,282999303,2073706907,true +2019,3,23,218038306,1080630611,false +2019,3,23,910132346,1175031905,true +2019,3,23,1618970409,846461653,true +2019,3,23,1894660459,1908413517,true +2019,3,23,861320716,974116872,false +2019,3,23,1434353599,347246297,false +2019,3,23,1681084098,502136029,true +2019,3,24,1141380248,1828336798,true +2019,3,24,366080224,208808425,true +2019,3,24,2038304283,512654473,true +2019,3,24,1232019923,1168241130,false +2019,3,24,953017069,1773810790,true +2019,3,24,899574335,1631146516,true +2019,3,24,806926551,1014740975,false +2019,3,24,1187719007,485636878,true +2019,3,24,295217482,208731799,false +2019,3,24,140015733,1126296810,true +2019,3,25,274134204,1163678153,true +2019,3,25,550645675,656717437,true +2019,3,25,2043352601,231452762,true +2019,3,25,1476080376,1649599709,true +2019,3,25,1677712826,1944223060,true +2019,3,25,767431902,624167417,false +2019,3,25,1443970973,14673773,false +2019,3,25,1486630435,424102268,false +2019,3,25,1651040985,721603524,true +2019,3,25,1562704087,1324786048,true +2019,3,26,735170687,91324859,true +2019,3,26,184756788,116013245,false +2019,3,26,1770352229,54939396,true +2019,3,26,444914765,1210134068,false +2019,3,26,1084837526,488311356,true +2019,3,26,1090474372,1679574666,true +2019,3,26,204068438,1289486255,false +2019,3,26,1122126638,1887482250,false +2019,3,26,1730701303,1084051216,false +2019,3,26,1902699013,217976462,true +2019,3,27,1741889790,115999311,true +2019,3,27,1750211121,2043306167,true +2019,3,27,1215984190,338812119,true +2019,3,27,1497849345,1665915969,true +2019,3,27,89090550,58501077,false +2019,3,27,1132818877,1556999256,true +2019,3,27,1028753435,196041556,true +2019,3,27,618242066,2098369842,true +2019,3,27,1587082059,868894041,false +2019,3,27,948609535,1878063201,true +2019,3,28,1102704928,1121847106,true +2019,3,28,1501458013,1951289749,true +2019,3,28,1566060260,832061203,true +2019,3,28,1915518023,1642123336,false +2019,3,28,965132234,1355138422,false +2019,3,28,1744398611,2128039693,true +2019,3,28,1762610231,731832986,false +2019,3,28,1139617187,563716978,true +2019,3,28,2082853326,2001111862,false +2019,3,28,2022273066,1151478203,false +2019,3,29,506205741,588704859,false +2019,3,29,784914111,8443358,true +2019,3,29,152149669,137304025,true +2019,3,29,251321431,721717127,false +2019,3,29,140717394,56939613,false +2019,3,29,1039621371,1620717841,false +2019,3,29,1477148762,1871002528,false +2019,3,29,1469130575,547856814,false +2019,3,29,1191832130,1096498740,true +2019,3,29,537669838,404095297,false +2019,3,30,470377107,84813779,false +2019,3,30,630251328,1834375309,false +2019,3,30,836924496,1896818755,true +2019,3,30,1263928666,2027449029,true +2019,3,30,462291935,245138464,true +2019,3,30,614203787,1562688941,true +2019,3,30,1278728312,1324725814,true +2019,3,30,58701137,107512765,true +2019,3,30,926627135,1853703472,true +2019,3,30,1312299203,1976254479,false +2019,3,31,1132342407,507294399,true +2019,3,31,1466880264,1261436414,true +2019,3,31,101827575,114000011,false +2019,3,31,267827691,297264825,false +2019,3,31,439159433,574891152,true +2019,3,31,1114870233,794455361,true +2019,3,31,1883437463,1354676655,false +2019,3,31,68566251,2089567617,false +2019,3,31,2138632308,1594921625,true +2019,3,31,1481715391,2083878283,true +2019,4,1,737402437,1869505643,false +2019,4,1,1498008659,1790908441,false +2019,4,1,1160400915,1404752772,false +2019,4,1,431279660,1861316063,true +2019,4,1,1696246093,1162385537,true +2019,4,1,1307387989,941928284,false +2019,4,1,1180130100,93029486,false +2019,4,1,1268551981,155992868,true +2019,4,1,150146027,1809396348,true +2019,4,1,444184829,1098489048,false +2019,4,2,1350031382,164987623,false +2019,4,2,2033677249,507946833,false +2019,4,2,816202327,1002036687,false +2019,4,2,232770483,1508853917,true +2019,4,2,1953008141,990119468,false +2019,4,2,1781137014,1757796576,false +2019,4,2,1945114386,802866223,true +2019,4,2,263198707,1768578372,true +2019,4,2,1388182136,2045653924,true +2019,4,2,270730566,305303153,true +2019,4,3,637682801,1896275507,false +2019,4,3,1862723403,819934786,false +2019,4,3,243052005,849037977,false +2019,4,3,998698929,1140839101,false +2019,4,3,854319207,1305878825,true +2019,4,3,1831566486,977321071,true +2019,4,3,791613826,1517798200,false +2019,4,3,1089422875,1355472062,true +2019,4,3,1016774295,493553211,true +2019,4,3,2131990136,923426002,true +2019,4,4,1201646791,684522699,false +2019,4,4,1435467880,1555509277,true +2019,4,4,1516827802,1369450179,true +2019,4,4,1555310873,1305347504,true +2019,4,4,628646754,1616533301,false +2019,4,4,2021609942,688427484,false +2019,4,4,1330703502,1424878060,true +2019,4,4,1199418150,1251694036,false +2019,4,4,1609421668,2105004975,true +2019,4,4,800452123,1225170245,true +2019,4,5,1737929184,982482826,false +2019,4,5,2111017477,835545632,true +2019,4,5,481303466,1987025089,true +2019,4,5,443089856,1579943186,false +2019,4,5,1630889050,25797691,true +2019,4,5,922474839,655422805,true +2019,4,5,1792579213,1190983044,true +2019,4,5,80046012,1673907422,true +2019,4,5,552336752,1852631742,true +2019,4,5,1026339252,1837403679,true +2019,4,6,1203131597,727498774,false +2019,4,6,1675013716,917833420,true +2019,4,6,43832263,1094747795,true +2019,4,6,1631826595,1609654116,false +2019,4,6,829514940,106179422,false +2019,4,6,1227762994,423616886,true +2019,4,6,1024952583,1043452885,true +2019,4,6,143862744,864200399,true +2019,4,6,331297191,1464365058,false +2019,4,6,1303454405,1963266584,false +2019,4,7,831868387,1303386196,true +2019,4,7,139424177,576105879,false +2019,4,7,596674442,624092326,true +2019,4,7,1059651289,443999998,false +2019,4,7,476817510,214120951,false +2019,4,7,1321156851,1745102741,false +2019,4,7,1006372596,942301543,false +2019,4,7,369100918,234982955,true +2019,4,7,688007296,1710188113,true +2019,4,7,128423361,1593517190,false +2019,4,8,552473912,547621527,true +2019,4,8,759160062,2070568012,false +2019,4,8,86750929,387377025,true +2019,4,8,1522307550,1287716765,true +2019,4,8,1603900630,951246522,false +2019,4,8,799966338,1481383900,false +2019,4,8,31746485,2126946895,false +2019,4,8,393104932,850065741,true +2019,4,8,1367536168,867083201,false +2019,4,8,789190501,1394003096,false +2019,4,9,1331760272,1148254596,false +2019,4,9,1953608517,590047061,true +2019,4,9,294211665,1124601734,true +2019,4,9,1730294068,1827080998,true +2019,4,9,1385547100,1083052682,true +2019,4,9,1915401664,1866573581,true +2019,4,9,1254613603,2129217416,false +2019,4,9,53062852,505706753,true +2019,4,9,1092453488,1151842118,false +2019,4,9,1088327975,1852128752,true +2019,4,10,197901520,1708594152,true +2019,4,10,290612621,140223656,true +2019,4,10,402401939,356667698,false +2019,4,10,898752144,194565225,false +2019,4,10,1571334932,186147235,true +2019,4,10,483916359,1736118791,false +2019,4,10,139563329,429636030,true +2019,4,10,636461656,779220706,false +2019,4,10,2050086304,796323858,true +2019,4,10,441306308,1773720358,false +2019,4,11,754704701,1016847336,true +2019,4,11,1540074783,2098120971,false +2019,4,11,524370953,506807711,true +2019,4,11,445627149,338723089,false +2019,4,11,1134289751,2018665242,false +2019,4,11,1996745729,1986729832,false +2019,4,11,852199282,2015053770,true +2019,4,11,1063013823,1929355527,false +2019,4,11,1689139072,121107268,false +2019,4,11,1743881234,294286111,true +2019,4,12,213588279,729551452,false +2019,4,12,379738023,579387879,false +2019,4,12,1740152332,895526458,true +2019,4,12,1753470237,532497407,true +2019,4,12,746142171,1632954858,true +2019,4,12,808141469,752082391,true +2019,4,12,69386054,2099300944,false +2019,4,12,1438957469,1294138580,true +2019,4,12,283139124,811492749,false +2019,4,12,298223006,905759798,false +2019,4,13,58072378,798236212,false +2019,4,13,975620604,347332074,true +2019,4,13,374761685,375270368,true +2019,4,13,967966765,1166462917,false +2019,4,13,1488001982,1329792524,true +2019,4,13,165347595,315683296,false +2019,4,13,809199036,293134138,true +2019,4,13,2062431980,1820232977,true +2019,4,13,1884637890,1661498832,false +2019,4,13,818261953,1020866735,true +2019,4,14,485707326,1441862957,true +2019,4,14,2092739958,835843364,false +2019,4,14,2186971,111054057,true +2019,4,14,1690192441,2077160699,true +2019,4,14,920135471,65631552,true +2019,4,14,317839744,862974060,false +2019,4,14,1648067594,2115444491,true +2019,4,14,1507338214,2112958361,false +2019,4,14,747871062,61168361,true +2019,4,14,155937584,1004000747,true +2019,4,15,674297554,1866507857,true +2019,4,15,952035609,898631008,false +2019,4,15,1895672848,1084528333,false +2019,4,15,501147324,620952225,true +2019,4,15,81530745,2026855063,true +2019,4,15,737846197,76858165,true +2019,4,15,1508393539,1813261115,true +2019,4,15,120834887,188802359,false +2019,4,15,322579358,1079339548,false +2019,4,15,930427697,175199314,false +2019,4,16,1047899014,682041318,true +2019,4,16,2130166520,1626927597,true +2019,4,16,1774133756,1323601751,true +2019,4,16,1763395535,531867350,false +2019,4,16,217730958,205506787,false +2019,4,16,1538431069,126513862,true +2019,4,16,268708989,2111733786,false +2019,4,16,2033258779,1569565331,true +2019,4,16,775432668,572726499,true +2019,4,16,2142432511,1442224114,false +2019,4,17,1980527746,323023334,false +2019,4,17,808165286,1786959379,true +2019,4,17,339530662,167573170,false +2019,4,17,2061233478,1675471996,true +2019,4,17,1258401138,64827277,false +2019,4,17,97868292,1582849516,false +2019,4,17,1183918595,1342529008,true +2019,4,17,491224463,182678325,true +2019,4,17,1132423553,598375438,false +2019,4,17,1836392709,1218284103,false +2019,4,18,1744559194,542913294,true +2019,4,18,1259838604,704950241,false +2019,4,18,1870927043,368950958,true +2019,4,18,1138556720,216389291,true +2019,4,18,739667724,1450302092,false +2019,4,18,1268053724,705308832,true +2019,4,18,69911153,1244148819,true +2019,4,18,1374584709,1482606606,false +2019,4,18,547257953,273728369,true +2019,4,18,806873066,945007518,true +2019,4,19,2007578747,840049190,false +2019,4,19,1736968051,1409787166,true +2019,4,19,216497,672457995,true +2019,4,19,661939211,1508893951,true +2019,4,19,1827792206,420857192,true +2019,4,19,724553751,2008644812,true +2019,4,19,1953623764,722702313,false +2019,4,19,2046330835,1553731845,false +2019,4,19,1242147815,1055521940,false +2019,4,19,1768839863,44181275,false +2019,4,20,214994379,1499934230,true +2019,4,20,364955833,1377359492,false +2019,4,20,1994240537,1175676342,true +2019,4,20,1591966847,1209347111,true +2019,4,20,2079567024,1224724119,true +2019,4,20,343147243,75486600,true +2019,4,20,1550687515,72740363,false +2019,4,20,821452155,1239530873,false +2019,4,20,773741531,809670695,true +2019,4,20,1201987699,338671173,true +2019,4,21,1806886997,926944987,false +2019,4,21,1842480853,1173695882,false +2019,4,21,2110831190,1961328434,false +2019,4,21,1767724427,1639629887,false +2019,4,21,51266156,610546579,true +2019,4,21,2087541840,439718294,false +2019,4,21,1330006660,392551184,true +2019,4,21,268185149,1145463226,true +2019,4,21,779099276,1567360294,false +2019,4,21,1004969221,158366115,false +2019,4,22,504358669,1092466308,true +2019,4,22,2026629439,1597497189,true +2019,4,22,1546246133,287610534,false +2019,4,22,633325404,1161161425,true +2019,4,22,1647010829,1187313524,true +2019,4,22,1624595470,1160086594,false +2019,4,22,77151689,1429319454,false +2019,4,22,1808432206,482346979,false +2019,4,22,2115840125,548714985,false +2019,4,22,816307273,1510527449,true +2019,4,23,1743793429,897690803,false +2019,4,23,806305241,786174991,true +2019,4,23,1310608353,1388608074,false +2019,4,23,727968336,2145472670,false +2019,4,23,1366731550,539147514,true +2019,4,23,1710564548,107592707,false +2019,4,23,632030260,1018030805,false +2019,4,23,210016542,1066758303,false +2019,4,23,1426964973,989685404,true +2019,4,23,262433173,347197752,false +2019,4,24,216756241,1841139807,true +2019,4,24,938422115,405359218,true +2019,4,24,950917406,376938537,false +2019,4,24,609180879,751690619,false +2019,4,24,1856440611,303661645,false +2019,4,24,2113388005,2023312478,false +2019,4,24,1515943141,22999097,false +2019,4,24,1526377073,98460857,false +2019,4,24,2053720782,2020141522,false +2019,4,24,1586276121,1091117001,false +2019,4,25,2044299105,2028950217,false +2019,4,25,348587180,421348107,true +2019,4,25,681728287,862198891,false +2019,4,25,215634941,1724207842,true +2019,4,25,511229990,10477525,true +2019,4,25,149131748,322381058,true +2019,4,25,1092400114,118386072,false +2019,4,25,1421054521,1880134288,true +2019,4,25,185042437,1238200113,true +2019,4,25,1450293738,1476244532,false +2019,4,26,981942882,1280316381,false +2019,4,26,102913910,823885978,true +2019,4,26,777422622,1755755950,false +2019,4,26,218115703,45720072,true +2019,4,26,1288258166,1117240568,false +2019,4,26,116909566,1327002135,true +2019,4,26,71437097,1260689258,false +2019,4,26,1042041580,1180914366,false +2019,4,26,862906634,89697636,false +2019,4,26,1866632853,693805593,false +2019,4,27,1543009281,1704766327,false +2019,4,27,151922505,1846893250,true +2019,4,27,1107541499,98797326,false +2019,4,27,830114711,119186582,true +2019,4,27,1384814707,802892976,true +2019,4,27,1885665900,23345714,true +2019,4,27,2111963287,410850135,false +2019,4,27,1262399827,990913943,true +2019,4,27,250799429,2062341079,true +2019,4,27,1724295051,673907373,false +2019,4,28,672964676,1224191051,true +2019,4,28,484241319,1356021924,true +2019,4,28,1693216905,874010355,false +2019,4,28,2025755380,1879888735,true +2019,4,28,1123259759,1454448927,true +2019,4,28,232906973,321256559,true +2019,4,28,409692964,1782221290,false +2019,4,28,55384579,296179530,false +2019,4,28,955819704,1425027569,false +2019,4,28,281403845,572569279,false +2019,4,29,445422745,1896307802,false +2019,4,29,368724580,1987752407,true +2019,4,29,2095900252,41025211,false +2019,4,29,1623551763,84053152,false +2019,4,29,2090980635,383594685,true +2019,4,29,577483915,689931677,true +2019,4,29,682456035,1556678040,false +2019,4,29,809878523,1364789961,false +2019,4,29,618496467,1461668029,false +2019,4,29,2117498225,1371799920,false +2019,4,30,1132482058,1094487100,true +2019,4,30,487978507,546353742,false +2019,4,30,1548210031,1236222676,false +2019,4,30,622580624,525672366,true +2019,4,30,754467059,1885376686,true +2019,4,30,262670920,571088263,false +2019,4,30,1432094444,1230824744,true +2019,4,30,491784994,1685160902,false +2019,4,30,2109291660,2061737288,false +2019,4,30,340049805,1925566784,false +2019,4,31,942477477,608088486,true +2019,4,31,2081957270,210215434,false +2019,4,31,451190589,1347492849,true +2019,4,31,1719667232,1624966691,false +2019,4,31,1786530203,1528627037,true +2019,4,31,1244985427,1536236982,true +2019,4,31,1612660949,1258554520,false +2019,4,31,1897102486,142341730,true +2019,4,31,839259911,1974244637,false +2019,4,31,74884784,268011840,false +2019,5,1,389630391,1920926453,false +2019,5,1,1275009513,2122611625,true +2019,5,1,1672209438,830800208,true +2019,5,1,548750132,212268259,true +2019,5,1,1874993729,773091196,true +2019,5,1,564677847,28707139,true +2019,5,1,760444452,2127178520,true +2019,5,1,1770548235,655292263,false +2019,5,1,344000126,142194594,false +2019,5,1,1860305022,2081836683,false +2019,5,2,814727860,10999979,false +2019,5,2,380698313,1023614259,true +2019,5,2,757402124,1355747668,true +2019,5,2,1519916597,1369705878,false +2019,5,2,1269806158,104933099,false +2019,5,2,1283276642,1705765269,false +2019,5,2,801478736,302880181,false +2019,5,2,897823917,1962930719,false +2019,5,2,870549794,198520394,true +2019,5,2,79761001,725734992,false +2019,5,3,205438168,1576214295,true +2019,5,3,1511337033,771585203,true +2019,5,3,805943324,1758810332,true +2019,5,3,1334381193,1840774503,true +2019,5,3,1800585232,2007270972,false +2019,5,3,635661499,630330909,false +2019,5,3,245401648,2009132478,true +2019,5,3,867744066,872175913,false +2019,5,3,2030363690,1402739406,true +2019,5,3,565200864,780631829,true +2019,5,4,1016186344,353203622,true +2019,5,4,931730149,267241086,true +2019,5,4,1293372999,1364423453,false +2019,5,4,1621884381,1995395108,false +2019,5,4,2085765931,477174987,false +2019,5,4,295570897,1123038576,true +2019,5,4,1513646059,1319956248,true +2019,5,4,1683607162,870165979,false +2019,5,4,526323024,483501560,true +2019,5,4,610731248,1683731453,false +2019,5,5,75121824,1622313614,false +2019,5,5,1569360831,1537332533,false +2019,5,5,901678435,1545601772,true +2019,5,5,2046236013,1914912540,true +2019,5,5,1315493865,1477237414,false +2019,5,5,1704028621,1523729481,false +2019,5,5,2048116407,986498549,false +2019,5,5,1706545079,1698465294,true +2019,5,5,1908017001,1558074012,true +2019,5,5,1919059967,575423284,false +2019,5,6,1397163949,1400938701,true +2019,5,6,2122962875,1086983143,false +2019,5,6,1097131349,347351997,false +2019,5,6,2113007425,134011206,true +2019,5,6,1090904259,1419734195,false +2019,5,6,777277533,722293597,true +2019,5,6,925605655,20941990,true +2019,5,6,36503984,1445689964,false +2019,5,6,78292448,2131982286,false +2019,5,6,1458384441,737607887,false +2019,5,7,1921903684,1187433401,false +2019,5,7,1753716595,1967978718,true +2019,5,7,380779998,640816062,true +2019,5,7,1592380824,1426529945,true +2019,5,7,1659898305,1492642016,false +2019,5,7,2073117913,1331660554,false +2019,5,7,671884377,813277572,false +2019,5,7,243820469,727183255,false +2019,5,7,381979952,1396349575,true +2019,5,7,1671333301,565630400,false +2019,5,8,1753252280,1572116117,false +2019,5,8,1287668529,1835138040,false +2019,5,8,1039506593,34703827,true +2019,5,8,310692342,925997727,true +2019,5,8,1449428345,1710503737,true +2019,5,8,836746196,612910150,true +2019,5,8,1793349239,2093455912,true +2019,5,8,251109258,290458303,false +2019,5,8,1780313793,51970601,true +2019,5,8,1328840071,889702066,true +2019,5,9,1666938264,483350490,false +2019,5,9,1535145332,2127369632,false +2019,5,9,1606872868,1165887849,true +2019,5,9,491490958,1337836196,true +2019,5,9,1308917209,975170098,false +2019,5,9,1217508396,1920101459,false +2019,5,9,1022481329,286766179,true +2019,5,9,2074346762,916219523,false +2019,5,9,1905065129,1287398565,true +2019,5,9,572073783,421634279,true +2019,5,10,1847337026,606971717,true +2019,5,10,678986671,1221972929,false +2019,5,10,1425254909,2058195793,true +2019,5,10,410317832,2059069975,false +2019,5,10,427559170,688573340,false +2019,5,10,1288231362,1291953451,false +2019,5,10,1823176059,441884344,false +2019,5,10,629270,853825330,true +2019,5,10,423327296,567129650,true +2019,5,10,994992726,537486192,false +2019,5,11,983878634,1381245632,true +2019,5,11,53393470,620396618,false +2019,5,11,814874227,1177478693,false +2019,5,11,2143485640,1841075184,true +2019,5,11,1209770202,628626667,false +2019,5,11,1559991131,635829921,true +2019,5,11,596083342,2010749257,true +2019,5,11,1645987960,768429510,true +2019,5,11,461572240,1643386323,true +2019,5,11,1508612644,1735342416,true +2019,5,12,852613253,1209479615,true +2019,5,12,911070665,65175832,false +2019,5,12,55406132,1909721577,false +2019,5,12,446587462,2134135354,false +2019,5,12,1517563936,709650646,false +2019,5,12,941928028,900029599,true +2019,5,12,199406142,1739461998,false +2019,5,12,1448991426,2145074993,false +2019,5,12,2093424127,392350498,false +2019,5,12,1793018504,2087943538,true +2019,5,13,1410579742,674142130,true +2019,5,13,1138274185,565992294,false +2019,5,13,849115375,1341143516,false +2019,5,13,2029107742,66049303,false +2019,5,13,1409634859,790888845,true +2019,5,13,2145262166,290454807,false +2019,5,13,1317696876,184642937,false +2019,5,13,2024722011,1363702220,true +2019,5,13,2132946638,1881645851,true +2019,5,13,555982027,1499716468,true +2019,5,14,726510455,656127373,false +2019,5,14,972972845,720344338,false +2019,5,14,1852228080,523619516,false +2019,5,14,2121555919,96686290,true +2019,5,14,904000154,261452258,false +2019,5,14,1726096299,1677378684,true +2019,5,14,1032259489,129556300,true +2019,5,14,391984167,349160723,false +2019,5,14,1370374200,587989204,false +2019,5,14,933488079,1402650807,false +2019,5,15,1330205962,1610098590,false +2019,5,15,532115540,1942277205,false +2019,5,15,1781003291,1707155184,true +2019,5,15,1264449910,1449889497,true +2019,5,15,1681334421,1992597823,false +2019,5,15,832579500,1984808410,false +2019,5,15,1608924984,222884813,true +2019,5,15,1096746651,1056178159,true +2019,5,15,2125927003,149093481,true +2019,5,15,1756395196,787023672,false +2019,5,16,1233031365,393037866,true +2019,5,16,1995986639,1120953025,false +2019,5,16,1895950542,777225029,true +2019,5,16,703520989,2123816898,true +2019,5,16,1894724990,1036099914,false +2019,5,16,718185980,351554536,true +2019,5,16,678297000,1953872486,false +2019,5,16,523303701,666959926,true +2019,5,16,1863685427,123157900,false +2019,5,16,839571035,1097572371,true +2019,5,17,646149907,207542403,false +2019,5,17,801103921,1046549160,true +2019,5,17,368163478,1267674702,false +2019,5,17,940123058,1984646294,true +2019,5,17,2003661082,1003450739,true +2019,5,17,1011427872,1450413742,false +2019,5,17,2055803204,963906307,false +2019,5,17,497201619,240540213,true +2019,5,17,2129308496,507900015,true +2019,5,17,247032978,1039537613,true +2019,5,18,1180075538,1871444371,false +2019,5,18,1160155730,1987855919,true +2019,5,18,1562019519,2046344579,false +2019,5,18,952921297,424600612,false +2019,5,18,413037978,636946419,false +2019,5,18,1389141382,1577176461,false +2019,5,18,1940480019,357313109,true +2019,5,18,858428127,608510296,false +2019,5,18,670110981,556602118,true +2019,5,18,1780333252,740136620,false +2019,5,19,1177653768,1272807963,true +2019,5,19,470962129,921181631,false +2019,5,19,770064666,336180915,false +2019,5,19,381318103,55993609,false +2019,5,19,1717873681,834525398,true +2019,5,19,1561294188,421906385,true +2019,5,19,149544840,1028391751,true +2019,5,19,357691330,1698743117,false +2019,5,19,972728650,255096276,true +2019,5,19,610384638,295626187,false +2019,5,20,738737776,478344205,true +2019,5,20,1874899129,1989562265,false +2019,5,20,1572549537,1922692454,true +2019,5,20,498002814,1757536165,false +2019,5,20,424549943,1171186476,false +2019,5,20,141537629,1179194900,true +2019,5,20,1920417600,668106205,false +2019,5,20,1203009768,278603063,false +2019,5,20,448811402,271283905,true +2019,5,20,342474040,275359154,true +2019,5,21,1186433597,417244107,false +2019,5,21,2132766930,274496679,true +2019,5,21,2120949682,6079293,true +2019,5,21,1282809977,699845446,false +2019,5,21,645844101,271047032,false +2019,5,21,717295187,1352320278,true +2019,5,21,902944026,274571008,false +2019,5,21,320670449,1757028043,false +2019,5,21,33858566,114722211,false +2019,5,21,1883333910,384563596,true +2019,5,22,1046162974,1770808119,true +2019,5,22,319504974,2142031599,true +2019,5,22,16812317,786767227,true +2019,5,22,58253754,1428520992,true +2019,5,22,829917207,873371123,false +2019,5,22,773324695,1027888866,false +2019,5,22,842684223,889511389,true +2019,5,22,1123851116,1715278188,false +2019,5,22,1047135396,423311755,false +2019,5,22,343479272,741804637,true +2019,5,23,450901483,1493565391,false +2019,5,23,728281800,821871664,false +2019,5,23,1058409274,378453300,false +2019,5,23,1018605678,268739574,true +2019,5,23,114814415,1603175484,false +2019,5,23,902773229,1133840709,false +2019,5,23,590796015,1408944863,true +2019,5,23,1257336230,2073510306,false +2019,5,23,1059321402,213906162,true +2019,5,23,1839817183,1927128006,false +2019,5,24,1278668420,680488530,true +2019,5,24,1883925970,1373993868,true +2019,5,24,499304130,809347290,true +2019,5,24,620488993,151579002,true +2019,5,24,1304026289,482496906,true +2019,5,24,1962325692,874414357,true +2019,5,24,1265187126,1326616951,true +2019,5,24,1531905939,300603268,false +2019,5,24,772951732,255593040,false +2019,5,24,536616623,1783982124,true +2019,5,25,190777286,41838497,true +2019,5,25,451236745,1526712142,true +2019,5,25,1955826460,693286170,false +2019,5,25,168257715,358161104,true +2019,5,25,550941554,720886146,false +2019,5,25,679762989,458543026,true +2019,5,25,2083324627,1215584548,false +2019,5,25,1014524936,1084919523,false +2019,5,25,664297136,1878141905,true +2019,5,25,174388503,1201020523,false +2019,5,26,1967863403,586863055,false +2019,5,26,1172988645,882062385,true +2019,5,26,1409378402,510801999,false +2019,5,26,1136684843,18208188,true +2019,5,26,1154570666,779499834,true +2019,5,26,1097055346,410633409,false +2019,5,26,1685115639,494396287,false +2019,5,26,153828019,131611232,false +2019,5,26,1835229529,480907295,true +2019,5,26,540675588,519824219,true +2019,5,27,477807584,1190553002,false +2019,5,27,2143596221,534265265,false +2019,5,27,107505481,1350868720,true +2019,5,27,1741946776,373765210,true +2019,5,27,820750133,1103561490,true +2019,5,27,1668739510,2097786908,false +2019,5,27,2003407014,511659059,true +2019,5,27,1863077779,2045566132,false +2019,5,27,1862124457,1542393718,false +2019,5,27,1213284168,1616684996,false +2019,5,28,1895424640,1767498778,true +2019,5,28,38685112,506893127,false +2019,5,28,1097484972,402348230,true +2019,5,28,1571948401,2121262966,true +2019,5,28,208122007,373427558,false +2019,5,28,349523305,35434598,false +2019,5,28,795947231,2069386536,true +2019,5,28,1891476196,1056330189,true +2019,5,28,1666749031,125003646,false +2019,5,28,1208368930,1923147684,true +2019,5,29,80829116,1638903852,true +2019,5,29,1807849637,508933287,false +2019,5,29,106760820,1668550091,true +2019,5,29,1927479603,1254201313,false +2019,5,29,37022086,2041647947,true +2019,5,29,988000480,32733383,false +2019,5,29,1606503384,1048869974,false +2019,5,29,1784500157,79027602,false +2019,5,29,336311408,1166362022,true +2019,5,29,286740746,926810572,true +2019,5,30,346343946,350818725,false +2019,5,30,1796185072,1978323797,false +2019,5,30,1987066378,1270873852,false +2019,5,30,1007344955,2114439762,false +2019,5,30,1408401137,1956555219,false +2019,5,30,484426372,1301195957,true +2019,5,30,130127344,943065580,false +2019,5,30,2144475850,1417381990,false +2019,5,30,527438634,1158876910,true +2019,5,30,76069237,1335269178,false +2019,5,31,223823542,1038782829,false +2019,5,31,1153552426,1980687920,false +2019,5,31,151147669,282933910,true +2019,5,31,1027444995,1113099701,true +2019,5,31,1448726542,932165004,false +2019,5,31,1752964613,1671666396,false +2019,5,31,465151782,1366336723,true +2019,5,31,1221157865,1028295114,true +2019,5,31,77432124,35373925,true +2019,5,31,1741966743,1773989834,false +2019,6,1,1601788906,2090998839,false +2019,6,1,100471517,175947711,true +2019,6,1,714803132,1878371428,true +2019,6,1,1060376653,244448758,false +2019,6,1,760814168,1892410165,true +2019,6,1,656729928,1412747396,true +2019,6,1,1149978161,712546492,true +2019,6,1,734751902,85143466,false +2019,6,1,611829723,196218561,false +2019,6,1,736459079,183246338,true +2019,6,2,159582097,1850192168,false +2019,6,2,105894953,670290530,false +2019,6,2,689674653,1798791720,false +2019,6,2,511735010,538684841,true +2019,6,2,1684819600,1818262497,false +2019,6,2,1504059152,1487848703,true +2019,6,2,1290484885,2059281458,false +2019,6,2,1935612556,502547764,true +2019,6,2,472523082,1095461088,true +2019,6,2,1908828308,915377713,false +2019,6,3,352862164,1844947318,false +2019,6,3,370775771,899788224,true +2019,6,3,1649246516,74934614,true +2019,6,3,985139442,289892127,true +2019,6,3,1008413964,1193840637,true +2019,6,3,809995657,1799637261,true +2019,6,3,147365701,1727442537,false +2019,6,3,1911498877,1707083352,true +2019,6,3,691403618,617015322,true +2019,6,3,796357547,312763668,false +2019,6,4,1832884321,1236790064,false +2019,6,4,1859954684,183777023,false +2019,6,4,1796327541,772982409,true +2019,6,4,1073190192,1208416713,false +2019,6,4,1949582854,322483697,false +2019,6,4,1734629958,1943570482,false +2019,6,4,265880495,1763769287,true +2019,6,4,1961157021,263961906,false +2019,6,4,2073358138,1791681690,true +2019,6,4,1399638813,1527554235,false +2019,6,5,1319598027,604171441,false +2019,6,5,1652087816,1396226473,false +2019,6,5,191620532,1193279614,false +2019,6,5,1830227784,560464634,false +2019,6,5,929947304,2005256289,false +2019,6,5,420563538,1042272878,true +2019,6,5,1490049866,1982016849,false +2019,6,5,983849460,1102794002,true +2019,6,5,976677709,296654661,false +2019,6,5,1697218473,647946865,false +2019,6,6,1392966555,1350246246,true +2019,6,6,1222792012,647053651,false +2019,6,6,1982330429,836755328,false +2019,6,6,95837757,2105237853,true +2019,6,6,1631262205,714800424,true +2019,6,6,1919315030,1654203112,false +2019,6,6,893368316,364436102,false +2019,6,6,1447783490,1896266472,true +2019,6,6,1889493930,592594902,true +2019,6,6,774614033,1307456195,false +2019,6,7,34192866,92108786,true +2019,6,7,945854503,1567671387,false +2019,6,7,1129821734,596715395,false +2019,6,7,1351463373,702961861,true +2019,6,7,1249740064,1987160491,true +2019,6,7,1983744021,582089821,true +2019,6,7,814422515,265795149,true +2019,6,7,291432651,112477489,true +2019,6,7,1565288464,306820408,true +2019,6,7,1706594474,2128052484,true +2019,6,8,1504196985,1086596829,true +2019,6,8,708258069,1647810131,true +2019,6,8,1060751810,1373931104,false +2019,6,8,1348251383,18278430,false +2019,6,8,813435952,548200992,true +2019,6,8,1448296864,168808044,true +2019,6,8,1258105795,808730374,false +2019,6,8,216736184,553149885,false +2019,6,8,24976912,795408452,false +2019,6,8,1119168415,883762658,false +2019,6,9,1641945648,1975812164,false +2019,6,9,1389002195,2069490945,true +2019,6,9,1440480059,1855619318,true +2019,6,9,652724010,300713382,true +2019,6,9,737548796,1727342790,false +2019,6,9,235821587,1464743722,false +2019,6,9,1036225512,926664410,true +2019,6,9,1578676497,470431205,true +2019,6,9,923600254,469992553,false +2019,6,9,1637346854,1965481182,true +2019,6,10,217738387,1880709707,true +2019,6,10,979706362,429985011,true +2019,6,10,1217954623,1536895252,false +2019,6,10,2007056596,210824631,false +2019,6,10,1997049443,1669883732,false +2019,6,10,981409474,960637556,true +2019,6,10,1019708162,337837270,false +2019,6,10,1058128258,1909436945,false +2019,6,10,1013099484,1011477231,false +2019,6,10,1232249361,859015885,true +2019,6,11,133098772,512893249,true +2019,6,11,411330791,2014413586,true +2019,6,11,1143430598,1298703229,false +2019,6,11,1116870463,1985085130,true +2019,6,11,1130688469,613138438,true +2019,6,11,942456077,366404858,false +2019,6,11,1576000605,1647892614,false +2019,6,11,1437376103,640504721,false +2019,6,11,1763481479,605025489,true +2019,6,11,1963545739,44847021,true +2019,6,12,1498986682,834033074,true +2019,6,12,1390925258,2054928071,false +2019,6,12,2086902871,712724584,true +2019,6,12,1364178177,1498108286,false +2019,6,12,1299520130,1452160263,true +2019,6,12,1328161895,1328784129,true +2019,6,12,1077973674,135639295,true +2019,6,12,116828373,1222113343,false +2019,6,12,93933005,443503717,true +2019,6,12,1798423000,557441133,false +2019,6,13,1465379242,844393438,true +2019,6,13,888760364,26979462,true +2019,6,13,319794522,319132223,true +2019,6,13,2105538415,299591105,false +2019,6,13,1009935376,242418742,true +2019,6,13,832516396,1872161601,false +2019,6,13,144520433,948514204,true +2019,6,13,459757201,1803625829,false +2019,6,13,360707891,1835068224,false +2019,6,13,1660955504,231180669,true +2019,6,14,1031358487,1414325582,true +2019,6,14,1695473746,544084700,true +2019,6,14,381566034,377095576,true +2019,6,14,208253643,1649503859,false +2019,6,14,96893177,410169969,false +2019,6,14,1817926226,1093004409,true +2019,6,14,616685599,223539673,false +2019,6,14,342451522,739504084,false +2019,6,14,1190157203,1814575655,true +2019,6,14,749423832,1737856319,true +2019,6,15,88521868,2035371130,false +2019,6,15,392663108,1205746775,false +2019,6,15,1792323873,549540498,true +2019,6,15,1785292333,1316752153,false +2019,6,15,1394247898,689138920,false +2019,6,15,1964431661,346652800,false +2019,6,15,2106357704,1593325330,false +2019,6,15,1256996666,237323579,false +2019,6,15,200428511,17979364,false +2019,6,15,383270138,222278067,false +2019,6,16,1262725765,1204307905,true +2019,6,16,1479130469,1485211352,false +2019,6,16,1136852119,1638654750,true +2019,6,16,969392974,1086466937,false +2019,6,16,2115288405,2097246252,false +2019,6,16,2108657064,863367686,false +2019,6,16,969538798,1445086738,false +2019,6,16,867664037,1289456401,true +2019,6,16,1938760102,571969875,false +2019,6,16,1717138676,139290350,false +2019,6,17,1066556109,156865572,true +2019,6,17,908837847,1783395642,false +2019,6,17,2125356798,1494387427,true +2019,6,17,1101099462,1762327352,true +2019,6,17,275776355,1735580663,false +2019,6,17,414485200,422785600,false +2019,6,17,1372687338,1495318451,true +2019,6,17,493153031,1815057846,false +2019,6,17,39998645,303556627,false +2019,6,17,1812306574,204098629,true +2019,6,18,1828270890,1734816124,false +2019,6,18,718628904,1316387408,true +2019,6,18,1850980713,1905908919,true +2019,6,18,338679387,1247976402,true +2019,6,18,1131671835,1048547293,true +2019,6,18,1816301182,1145492016,false +2019,6,18,125582032,70266620,true +2019,6,18,429634125,1064398494,true +2019,6,18,1097880933,820369039,true +2019,6,18,582901724,951221294,true +2019,6,19,1049179442,1609347371,false +2019,6,19,1360911611,1575728286,true +2019,6,19,1642386662,228873957,true +2019,6,19,260830831,489927038,false +2019,6,19,257202781,815575136,false +2019,6,19,318012546,1314086734,true +2019,6,19,1613027294,1612073096,false +2019,6,19,722745822,1751468696,true +2019,6,19,1974103665,1376673632,false +2019,6,19,2045549240,1083465534,false +2019,6,20,176959579,48119153,true +2019,6,20,990071202,1122480232,false +2019,6,20,689662693,1045236407,true +2019,6,20,782015662,697494496,true +2019,6,20,1831372008,2121209126,true +2019,6,20,1484036259,80135608,false +2019,6,20,2137519070,396218902,true +2019,6,20,298443116,2052915600,true +2019,6,20,410155890,1006848825,false +2019,6,20,519239189,860050651,true +2019,6,21,1927728050,345629420,false +2019,6,21,1140122600,430784997,true +2019,6,21,557756538,30915838,false +2019,6,21,1120731025,1528614298,false +2019,6,21,883896503,1928491074,false +2019,6,21,32013384,752198199,true +2019,6,21,407946882,1546928779,true +2019,6,21,169195639,1855872868,false +2019,6,21,58610001,7726044,false +2019,6,21,642128986,152867953,true +2019,6,22,1772689841,1266530038,false +2019,6,22,752632451,1157938097,true +2019,6,22,1049379387,1796539534,false +2019,6,22,1101144913,609719833,false +2019,6,22,704635849,695880651,true +2019,6,22,133493678,1385172151,true +2019,6,22,151839510,1160383965,true +2019,6,22,756589761,559567262,true +2019,6,22,1948703819,1918518220,true +2019,6,22,525944136,301984849,true +2019,6,23,1848466736,1684206806,false +2019,6,23,563363548,1693040097,false +2019,6,23,1149018345,1346138349,false +2019,6,23,1928026196,294479660,true +2019,6,23,406792342,346773043,true +2019,6,23,932043759,679929862,true +2019,6,23,914059820,621220239,false +2019,6,23,663554851,1660508212,false +2019,6,23,246268764,512346540,false +2019,6,23,454392600,466965372,true +2019,6,24,737611879,634054908,true +2019,6,24,3791924,1479780414,false +2019,6,24,1872004908,785880730,false +2019,6,24,767122506,433921099,false +2019,6,24,118736092,398438178,true +2019,6,24,1416500810,280641015,true +2019,6,24,160319757,365495763,false +2019,6,24,379726723,925976478,true +2019,6,24,463659820,679359380,true +2019,6,24,1406992847,1196730893,true +2019,6,25,317239158,573033665,true +2019,6,25,1345849164,1465314961,true +2019,6,25,2057260804,1420677055,true +2019,6,25,1888163689,2021907356,false +2019,6,25,1661037204,480792005,true +2019,6,25,1696431729,350891836,false +2019,6,25,1866387974,422052969,true +2019,6,25,688943067,404449499,false +2019,6,25,461093591,1537583563,true +2019,6,25,729997571,254414848,false +2019,6,26,1890799094,1574854256,false +2019,6,26,1381166367,1682192392,false +2019,6,26,393277891,1385160494,true +2019,6,26,688226443,1124909329,true +2019,6,26,2055804711,1590951616,true +2019,6,26,330225856,98473105,true +2019,6,26,1761532150,1597141455,false +2019,6,26,1102822288,67233861,true +2019,6,26,130941732,1635606933,true +2019,6,26,1871180669,907803780,true +2019,6,27,1071852405,1659983985,true +2019,6,27,637005900,727245596,true +2019,6,27,924252066,1930838259,true +2019,6,27,1306276380,2004300345,false +2019,6,27,141266235,611134295,true +2019,6,27,1928442538,1394420495,false +2019,6,27,391120294,1245097571,true +2019,6,27,736928652,153837118,false +2019,6,27,388345092,938397728,false +2019,6,27,1579540868,1013139486,false +2019,6,28,54877427,1516617712,true +2019,6,28,1822964799,1479626115,false +2019,6,28,631995479,801513187,false +2019,6,28,1494972186,397737365,false +2019,6,28,1279370167,920218721,false +2019,6,28,511148742,467408839,false +2019,6,28,98806226,44973190,false +2019,6,28,244592854,852204830,false +2019,6,28,1463320182,692089528,false +2019,6,28,899111341,514636354,true +2019,6,29,894880187,311273060,true +2019,6,29,843409983,1733536781,false +2019,6,29,469390390,1570531564,false +2019,6,29,1134322329,303985300,false +2019,6,29,1074649284,717695776,false +2019,6,29,2085013307,223168231,true +2019,6,29,388012780,1276595682,false +2019,6,29,641709729,641983982,false +2019,6,29,873399581,135023811,false +2019,6,29,280070538,385937204,false +2019,6,30,1845508135,279001778,false +2019,6,30,1182330858,1884250240,true +2019,6,30,976873920,1568196637,true +2019,6,30,498380159,1991290759,true +2019,6,30,70894076,893390616,true +2019,6,30,241746054,1036957986,true +2019,6,30,1631365112,1760705882,false +2019,6,30,1575430657,90816971,false +2019,6,30,615885873,43267384,false +2019,6,30,905089678,431859014,false +2019,6,31,1373604660,345388515,false +2019,6,31,2099441602,149103568,true +2019,6,31,1077718446,963303575,true +2019,6,31,1683680637,618105118,true +2019,6,31,993338123,699138936,true +2019,6,31,1257276350,1248445973,true +2019,6,31,178973216,193408086,true +2019,6,31,504601808,537137162,false +2019,6,31,1872297872,72823947,false +2019,6,31,969167095,1018904792,true +2019,7,1,1630972911,1079614640,false +2019,7,1,978179037,513284420,true +2019,7,1,990870488,191535584,true +2019,7,1,222563695,681327452,false +2019,7,1,547994211,124061623,true +2019,7,1,422955760,1252967631,true +2019,7,1,785203912,1853561186,false +2019,7,1,1320930918,573567354,true +2019,7,1,905877522,81221963,false +2019,7,1,1931645356,1213508765,true +2019,7,2,1588427300,317508168,false +2019,7,2,1159178197,1910145900,false +2019,7,2,1456551309,2023499356,false +2019,7,2,2143818400,1893832192,false +2019,7,2,845791189,1617696361,false +2019,7,2,1080029995,487062485,false +2019,7,2,364700747,1814897505,true +2019,7,2,64521257,2129340243,true +2019,7,2,2009032059,995613916,true +2019,7,2,521902407,1003370516,false +2019,7,3,1633129942,1658688024,false +2019,7,3,1757266568,150302185,false +2019,7,3,1116837428,1632686548,false +2019,7,3,22870966,1514675915,false +2019,7,3,1732031284,43188864,true +2019,7,3,912936772,1437351990,true +2019,7,3,1992839851,917346234,false +2019,7,3,819681582,1174196394,true +2019,7,3,398989465,1393492756,false +2019,7,3,804581825,876791869,false +2019,7,4,459829092,205147532,true +2019,7,4,1251199307,48611201,true +2019,7,4,1996226891,1733991497,false +2019,7,4,33633569,1859437114,true +2019,7,4,23658822,1621986933,false +2019,7,4,876751753,1324537734,true +2019,7,4,601026517,1120777034,true +2019,7,4,857882459,126827408,false +2019,7,4,2036261994,31202990,false +2019,7,4,1104389506,1485055716,false +2019,7,5,892395120,1758572531,false +2019,7,5,598523291,1815928309,true +2019,7,5,326584611,1412415893,true +2019,7,5,604081257,1249161092,false +2019,7,5,855983022,1998586380,true +2019,7,5,1762648538,1658065628,true +2019,7,5,1806070606,75298060,true +2019,7,5,617403846,1765953311,false +2019,7,5,255440745,1025139143,false +2019,7,5,839691709,756255523,true +2019,7,6,849876955,1447464468,false +2019,7,6,1339517812,1994058117,true +2019,7,6,1694450667,590604156,false +2019,7,6,1648232114,1431566078,false +2019,7,6,1236468892,1879929513,true +2019,7,6,1054473944,1608466728,false +2019,7,6,1244573535,712464036,false +2019,7,6,1838894921,599521828,false +2019,7,6,1557265998,95808736,false +2019,7,6,1290942307,872425964,false +2019,7,7,1308510584,2034776084,false +2019,7,7,624067024,1295362104,true +2019,7,7,1425015014,395531071,false +2019,7,7,964581451,633012786,true +2019,7,7,243927392,248094587,false +2019,7,7,243664947,1025709434,true +2019,7,7,1359891998,1888354909,true +2019,7,7,737196422,979803683,true +2019,7,7,466683876,1830729757,false +2019,7,7,872795411,739887023,false +2019,7,8,336524863,1616307131,false +2019,7,8,1488367624,343544122,true +2019,7,8,924688862,188642514,true +2019,7,8,1212301861,421729102,false +2019,7,8,608094001,1559690722,true +2019,7,8,456900925,1702560277,false +2019,7,8,965463778,1016397626,true +2019,7,8,1543198669,1281470700,true +2019,7,8,1368772032,981518782,false +2019,7,8,1836793289,1431401597,false +2019,7,9,1875605548,644530754,true +2019,7,9,709665275,835393258,false +2019,7,9,968742193,897337658,true +2019,7,9,297246591,807089588,true +2019,7,9,882039754,1547840476,true +2019,7,9,993207877,220029789,true +2019,7,9,1536332038,1444289308,false +2019,7,9,974145184,1145132798,false +2019,7,9,1119307782,2085782011,false +2019,7,9,1824558918,94429758,true +2019,7,10,960453901,610600995,false +2019,7,10,1259041365,1187406450,false +2019,7,10,600881943,657942221,false +2019,7,10,734183734,483818451,true +2019,7,10,781893551,713082206,true +2019,7,10,755208303,1036269059,true +2019,7,10,1558655752,286910525,false +2019,7,10,381029323,1731368797,true +2019,7,10,1932008056,2095572944,false +2019,7,10,1140605022,790409418,false +2019,7,11,1747892712,708916904,true +2019,7,11,2126738767,1974353412,false +2019,7,11,444347618,370296130,true +2019,7,11,955557231,632856214,true +2019,7,11,361331251,274620127,true +2019,7,11,1133763330,982419968,true +2019,7,11,1424067943,474496616,true +2019,7,11,43639883,2023001527,true +2019,7,11,1269131137,340239197,true +2019,7,11,623108787,1334095368,true +2019,7,12,725982406,36613688,false +2019,7,12,679864236,915110662,false +2019,7,12,492128110,812994762,true +2019,7,12,465828250,662118656,false +2019,7,12,384858004,1881832070,false +2019,7,12,1262209121,915111993,true +2019,7,12,1249584841,1051404720,false +2019,7,12,256564344,924760542,false +2019,7,12,898398727,403571172,true +2019,7,12,521434245,1072230502,true +2019,7,13,767330049,370066813,true +2019,7,13,1551394034,493406174,false +2019,7,13,785639936,1252909753,true +2019,7,13,1888186876,672698069,false +2019,7,13,814141388,1639743161,true +2019,7,13,802789418,2072081127,false +2019,7,13,1360444247,692180897,true +2019,7,13,1366063,1819921419,false +2019,7,13,434426138,1912196232,false +2019,7,13,158216863,1959260220,false +2019,7,14,1190322795,538551556,true +2019,7,14,840621320,1850094868,false +2019,7,14,713843001,1655513955,false +2019,7,14,689646919,1022908263,false +2019,7,14,1364058345,2121094138,false +2019,7,14,942838065,1853063440,true +2019,7,14,1167942473,671660307,true +2019,7,14,450689003,2127373155,false +2019,7,14,253655260,892024414,false +2019,7,14,440706953,93972737,false +2019,7,15,2047459811,966182895,true +2019,7,15,1072653507,461155337,true +2019,7,15,1730261494,1172140080,true +2019,7,15,1552966631,318813651,true +2019,7,15,1086859782,458435927,false +2019,7,15,687898962,1946493885,false +2019,7,15,544120093,676594225,false +2019,7,15,22285586,1678717101,true +2019,7,15,537498178,930578288,true +2019,7,15,977197187,1282663769,true +2019,7,16,205554234,1525649850,true +2019,7,16,1170701825,885547383,false +2019,7,16,401332776,1096021537,true +2019,7,16,1038801413,1736121946,true +2019,7,16,1870619710,460614108,false +2019,7,16,1228059216,552355757,true +2019,7,16,298978801,1967178574,false +2019,7,16,1380344548,1106977098,false +2019,7,16,600508277,376730046,true +2019,7,16,897804802,1444510051,false +2019,7,17,1424300653,1520481876,true +2019,7,17,361538409,853732462,true +2019,7,17,1616176655,383915059,false +2019,7,17,169492606,1746293912,true +2019,7,17,1619393839,1851213510,false +2019,7,17,1665654324,137736368,true +2019,7,17,1625931500,1199395995,false +2019,7,17,920179396,1400975039,false +2019,7,17,753712375,624517734,false +2019,7,17,723631255,911828175,true +2019,7,18,2137762473,164072700,true +2019,7,18,1174917360,785067659,false +2019,7,18,722318944,975280742,true +2019,7,18,1735275573,257208517,true +2019,7,18,2146034408,1531023713,false +2019,7,18,1119164145,533838844,false +2019,7,18,1936263372,162368480,false +2019,7,18,40909276,1150336377,true +2019,7,18,733718500,254489714,false +2019,7,18,1307519114,736539304,false +2019,7,19,937308044,25403283,false +2019,7,19,1240645023,1129165990,true +2019,7,19,851963956,436889940,false +2019,7,19,1124379786,1390395978,false +2019,7,19,457184083,697662689,true +2019,7,19,579316792,712965388,true +2019,7,19,498323167,1512799940,false +2019,7,19,1357628232,2048502161,true +2019,7,19,846471133,778357765,true +2019,7,19,1943138717,343310037,false +2019,7,20,1313717154,911421295,false +2019,7,20,1483625570,75249442,true +2019,7,20,1520511451,1394418585,true +2019,7,20,921614560,1877245002,true +2019,7,20,796141950,1597932436,false +2019,7,20,317566902,112737519,true +2019,7,20,1050226979,1822375240,false +2019,7,20,1419601229,1853606583,false +2019,7,20,1227495301,982237149,false +2019,7,20,1203838369,1970332092,false +2019,7,21,1736133398,849268176,false +2019,7,21,1131217086,876640156,false +2019,7,21,449611749,2072173947,true +2019,7,21,94386807,67501295,false +2019,7,21,1640579409,1693817915,true +2019,7,21,2084117314,224994837,false +2019,7,21,2057832757,690702208,true +2019,7,21,762526019,1753573528,true +2019,7,21,1024897175,680244318,false +2019,7,21,1172264942,1779019916,true +2019,7,22,163954176,2121259393,true +2019,7,22,1938319809,36009409,true +2019,7,22,692958207,602787987,true +2019,7,22,630334211,716692126,false +2019,7,22,988515984,1644256109,true +2019,7,22,114269593,1171451639,true +2019,7,22,1837590618,1864390323,true +2019,7,22,147082562,675356490,true +2019,7,22,1455124733,359855784,true +2019,7,22,446253277,370234649,true +2019,7,23,1910821864,1605272662,false +2019,7,23,1436321931,513468257,true +2019,7,23,316273525,385048683,true +2019,7,23,1236302277,854344289,false +2019,7,23,737082939,950183503,false +2019,7,23,1215234493,1488237610,false +2019,7,23,827094221,742962528,false +2019,7,23,2025587625,306519984,false +2019,7,23,1275806827,1228765026,false +2019,7,23,1831244746,1274939988,false +2019,7,24,236173129,1234453113,false +2019,7,24,618953024,849385503,false +2019,7,24,2126722428,606601800,false +2019,7,24,604917785,797452578,true +2019,7,24,229095849,1612625681,false +2019,7,24,705286680,1224439490,true +2019,7,24,641565452,1186767590,true +2019,7,24,1062164513,2146294226,false +2019,7,24,2108031605,1917090887,false +2019,7,24,480040790,526310079,true +2019,7,25,1670369857,1411449545,false +2019,7,25,586564675,383378254,true +2019,7,25,1274792329,650980759,false +2019,7,25,230011823,472378768,false +2019,7,25,751270130,1568943334,false +2019,7,25,1773048807,1465839127,true +2019,7,25,240757732,281571002,true +2019,7,25,125977254,1803519893,false +2019,7,25,535393045,388568208,true +2019,7,25,1574560475,261743726,true +2019,7,26,1712248237,1186306752,true +2019,7,26,1944320775,1133670110,false +2019,7,26,754457705,876030905,true +2019,7,26,1316741097,1082792644,false +2019,7,26,29740679,2105739371,true +2019,7,26,2051969188,50011191,true +2019,7,26,92764773,1893196909,false +2019,7,26,451100855,1994968164,false +2019,7,26,692409367,789897166,false +2019,7,26,1542302031,767177332,false +2019,7,27,1070539360,1848719756,true +2019,7,27,742600419,1196669024,false +2019,7,27,495709907,1640152358,false +2019,7,27,1896431897,1918047230,true +2019,7,27,297469954,1704106696,false +2019,7,27,1658317407,630516409,false +2019,7,27,329067788,735485177,true +2019,7,27,1204656585,879116959,true +2019,7,27,242925547,854251487,true +2019,7,27,1643420938,242482643,true +2019,7,28,6533183,1154403693,false +2019,7,28,2118439327,380859786,false +2019,7,28,179896993,966453017,true +2019,7,28,1152883600,2106286103,true +2019,7,28,1382250649,1396170987,true +2019,7,28,1588952395,168399279,false +2019,7,28,691764159,2100470533,true +2019,7,28,2036174724,559311338,true +2019,7,28,14424776,446881269,false +2019,7,28,2046484776,328396861,true +2019,7,29,258814301,1490672496,true +2019,7,29,612583100,793989071,false +2019,7,29,564841482,1724328004,true +2019,7,29,101230014,1231069048,false +2019,7,29,662966202,1331254694,false +2019,7,29,358163436,753286360,true +2019,7,29,100167791,948363046,false +2019,7,29,317006711,441944509,false +2019,7,29,2026661169,1512347628,true +2019,7,29,286944390,1340678482,false +2019,7,30,1674057586,881831190,true +2019,7,30,1027252640,1827581651,true +2019,7,30,510351963,581868606,false +2019,7,30,1548087249,666763697,false +2019,7,30,1846448824,49366173,true +2019,7,30,1181820067,952490868,true +2019,7,30,1843114624,100189766,true +2019,7,30,878494536,1032835513,false +2019,7,30,945558312,1350436456,true +2019,7,30,1716496260,675318556,false +2019,7,31,1757831236,1508685981,false +2019,7,31,1787760694,74459119,true +2019,7,31,344782892,258051243,false +2019,7,31,481090713,1665196049,false +2019,7,31,635615887,1978590534,false +2019,7,31,2147364495,1349408064,false +2019,7,31,581909829,1095505845,false +2019,7,31,972870030,689626547,false +2019,7,31,1115708861,661714279,true +2019,7,31,1146521873,853454063,true +2019,8,1,1536045526,1411454933,false +2019,8,1,382341785,1210077446,true +2019,8,1,108987656,1197499466,false +2019,8,1,1085998747,1779370181,false +2019,8,1,1868982057,2005028973,false +2019,8,1,1940269305,721032062,true +2019,8,1,1460070450,962521492,true +2019,8,1,546239694,2010633587,true +2019,8,1,1247805638,1461211220,true +2019,8,1,135696566,698586122,false +2019,8,2,361654647,2030014149,false +2019,8,2,1088073698,622277451,true +2019,8,2,30790390,408697712,false +2019,8,2,1186496916,59363940,false +2019,8,2,417341362,386698224,true +2019,8,2,1125286859,2087581472,true +2019,8,2,1449393043,472261012,true +2019,8,2,310847320,71776407,false +2019,8,2,421955790,1616857019,true +2019,8,2,439255693,751605627,false +2019,8,3,24699154,1369333400,false +2019,8,3,1813849407,1660550865,true +2019,8,3,113307761,1817515674,true +2019,8,3,1854229468,1757434368,true +2019,8,3,1206895571,1520943031,true +2019,8,3,959676603,662898027,false +2019,8,3,974083811,833131847,true +2019,8,3,1190855159,574559519,true +2019,8,3,139742632,246114862,true +2019,8,3,197480304,1867993399,false +2019,8,4,1756980292,145799814,true +2019,8,4,1108404166,1747646975,false +2019,8,4,1628621731,1092534034,true +2019,8,4,1818281071,523671049,false +2019,8,4,259437551,1492909080,false +2019,8,4,1752785971,1798068903,true +2019,8,4,1934163493,40286322,false +2019,8,4,1938520242,642612105,true +2019,8,4,239928361,422519548,true +2019,8,4,440886982,387174511,false +2019,8,5,2020059940,1159953325,true +2019,8,5,469306072,1548388407,true +2019,8,5,617813519,1648143085,false +2019,8,5,417294724,1425254055,false +2019,8,5,1302159036,212999445,true +2019,8,5,1911058978,217636543,false +2019,8,5,747456959,498256153,false +2019,8,5,1203473417,348818822,false +2019,8,5,509019642,756142277,false +2019,8,5,1049469102,1934375296,true +2019,8,6,1294680773,1631811103,false +2019,8,6,1981514725,472881493,true +2019,8,6,21188580,227869888,true +2019,8,6,1211781362,699256919,true +2019,8,6,697049288,1259018060,true +2019,8,6,555507252,1177672157,false +2019,8,6,527565005,673062988,false +2019,8,6,1413618300,134878385,true +2019,8,6,1899567222,968336150,false +2019,8,6,1639140993,667686414,true +2019,8,7,2107679703,995812493,true +2019,8,7,2107320809,266039249,true +2019,8,7,1757182125,1959484632,true +2019,8,7,568461020,1076328432,true +2019,8,7,1254770377,1757456997,true +2019,8,7,1044264995,360083294,true +2019,8,7,2015193067,1259045840,true +2019,8,7,963794233,481178831,false +2019,8,7,1725967380,204929419,true +2019,8,7,1753586911,1785728048,false +2019,8,8,169272824,989630806,true +2019,8,8,1775895052,1390509475,false +2019,8,8,1332584224,1285895402,false +2019,8,8,1283758166,827783004,true +2019,8,8,1956306385,1650123359,false +2019,8,8,198650915,1564754878,false +2019,8,8,403789532,926636092,false +2019,8,8,180695107,1835342206,true +2019,8,8,68703192,285730866,true +2019,8,8,220241348,1236704697,true +2019,8,9,1214976645,747438824,false +2019,8,9,625753118,864464966,false +2019,8,9,1661860923,1764310122,true +2019,8,9,40687368,244570273,false +2019,8,9,1385979062,1001475097,true +2019,8,9,1483766935,1800975942,true +2019,8,9,250660259,1148728364,false +2019,8,9,928369014,621248861,true +2019,8,9,721920177,1976638495,true +2019,8,9,574282325,1221624058,true +2019,8,10,2123899934,356372137,false +2019,8,10,1803452643,1520169571,true +2019,8,10,1573977581,1092683530,false +2019,8,10,938158064,2019213593,true +2019,8,10,2105026638,1936726742,true +2019,8,10,807089546,1581199772,false +2019,8,10,1057523168,779281541,true +2019,8,10,1897792301,1906856220,false +2019,8,10,104050386,1662742635,false +2019,8,10,1943241575,1579231892,true +2019,8,11,429248526,2107023600,false +2019,8,11,1982706041,122912268,false +2019,8,11,1589588981,1676875802,true +2019,8,11,1378468569,673222887,true +2019,8,11,356061267,2078868418,false +2019,8,11,788599720,1028175504,true +2019,8,11,883365782,1641168658,true +2019,8,11,1080147576,2117727976,true +2019,8,11,528358253,586311454,false +2019,8,11,265358575,1269419062,false +2019,8,12,424828247,1717522867,false +2019,8,12,25969203,399959187,false +2019,8,12,345114061,873897139,true +2019,8,12,1438513440,972763906,false +2019,8,12,814560522,1277761939,false +2019,8,12,790904750,1211169461,false +2019,8,12,2110539935,525499353,true +2019,8,12,211553828,1561258805,true +2019,8,12,528799576,1589000881,false +2019,8,12,550339547,897543770,true +2019,8,13,849910040,1729811690,false +2019,8,13,761709758,328270175,true +2019,8,13,1407746740,408742905,true +2019,8,13,360987095,2090035320,true +2019,8,13,1613159936,1071891934,false +2019,8,13,1083221119,664129704,false +2019,8,13,1650041984,965871352,false +2019,8,13,1881650952,488773082,true +2019,8,13,1078652800,803646506,true +2019,8,13,69603077,827487067,false +2019,8,14,1559722622,217303777,true +2019,8,14,1510100084,2088858446,false +2019,8,14,436604204,11022298,true +2019,8,14,313976440,377228724,false +2019,8,14,1189132409,1927671488,true +2019,8,14,620960100,458170899,true +2019,8,14,1601189460,1892099053,true +2019,8,14,947137350,1424678636,false +2019,8,14,1681915444,107077373,true +2019,8,14,1175329594,2123812989,true +2019,8,15,2132791432,117454834,true +2019,8,15,1864625438,608846978,false +2019,8,15,404629907,1843872276,false +2019,8,15,1666315919,181928683,false +2019,8,15,1004685418,1884403424,true +2019,8,15,582953771,1142365131,true +2019,8,15,1443352840,1853759635,false +2019,8,15,1681536400,922831083,false +2019,8,15,1180211384,1879428922,false +2019,8,15,1190821779,254946749,false +2019,8,16,1037136449,784157781,true +2019,8,16,362280858,727263915,true +2019,8,16,2085020570,308355172,true +2019,8,16,618902063,1729925368,true +2019,8,16,665854224,1946477309,false +2019,8,16,775573013,2090556067,true +2019,8,16,1543408168,1057392960,true +2019,8,16,1986236044,1557147158,false +2019,8,16,1722991259,265684918,false +2019,8,16,1806353038,847968129,false +2019,8,17,359558719,672166925,false +2019,8,17,387225890,2119094836,false +2019,8,17,329916613,1436954665,true +2019,8,17,895074841,1797319254,true +2019,8,17,139807366,1403722625,true +2019,8,17,364675028,481721281,true +2019,8,17,1392271836,1308467391,true +2019,8,17,192539109,2089790719,true +2019,8,17,906906812,149062948,false +2019,8,17,1930387880,556387719,true +2019,8,18,1358606468,291370009,true +2019,8,18,1331196193,282330325,false +2019,8,18,666394180,1654271216,true +2019,8,18,544819656,1969738744,true +2019,8,18,39198692,1066846669,false +2019,8,18,980167694,2041871768,true +2019,8,18,1699530483,1451466279,false +2019,8,18,2142992825,566659258,false +2019,8,18,2105743375,2115237986,false +2019,8,18,685499493,1546104986,true +2019,8,19,1641682457,1990489460,false +2019,8,19,2037931738,2074591349,true +2019,8,19,913888949,1016192654,false +2019,8,19,2032141036,1247519784,true +2019,8,19,717520334,1943242821,true +2019,8,19,1485983520,800091868,false +2019,8,19,1283772427,1119989679,false +2019,8,19,1843578452,1011273534,false +2019,8,19,1441074302,63450472,true +2019,8,19,700868880,541486330,false +2019,8,20,35650552,637589384,false +2019,8,20,916292437,1678085483,false +2019,8,20,719679672,1462100709,false +2019,8,20,2104374013,761343423,false +2019,8,20,2098617217,1134285117,true +2019,8,20,2145954317,1032654594,false +2019,8,20,294007603,278514971,true +2019,8,20,1141888388,617483467,true +2019,8,20,2048868983,493227409,false +2019,8,20,1665633429,481235331,true +2019,8,21,249140002,1994836766,false +2019,8,21,547228997,468263802,false +2019,8,21,645869919,2063451230,true +2019,8,21,1890067173,1497885768,false +2019,8,21,1629843642,1843403856,false +2019,8,21,473837540,62378032,false +2019,8,21,347229090,1427885397,false +2019,8,21,1192904682,335199604,true +2019,8,21,1397944833,521261886,true +2019,8,21,710664256,1217027046,true +2019,8,22,179353078,1226103800,true +2019,8,22,799615974,911590169,true +2019,8,22,399691225,1601278397,true +2019,8,22,2147438066,219048438,true +2019,8,22,1365010700,1953675990,true +2019,8,22,302315528,601566377,true +2019,8,22,904489292,86592070,false +2019,8,22,1644436488,139206180,true +2019,8,22,1498563707,310993413,false +2019,8,22,134864716,913895584,false +2019,8,23,1437479706,2090926734,false +2019,8,23,1379158787,614737133,false +2019,8,23,1777233389,1399129317,true +2019,8,23,678238356,1286894191,false +2019,8,23,1827531137,1872483178,true +2019,8,23,1588314639,1171065363,true +2019,8,23,538331273,195261203,false +2019,8,23,858750472,1651836075,false +2019,8,23,332379724,1774690786,true +2019,8,23,1485198209,323127657,true +2019,8,24,193499296,1392273580,false +2019,8,24,824757675,1932170103,true +2019,8,24,567889183,1019628947,false +2019,8,24,1240684695,1977206236,false +2019,8,24,256796571,1252633028,false +2019,8,24,1682818879,357303087,false +2019,8,24,1425438721,1606045771,false +2019,8,24,798965867,947491523,true +2019,8,24,1823366181,1355351334,false +2019,8,24,1702798170,1074990934,true +2019,8,25,1253883435,83888863,false +2019,8,25,557100601,797850472,true +2019,8,25,1005894293,829921869,true +2019,8,25,1207082953,758232514,false +2019,8,25,1250169104,1094204239,false +2019,8,25,935592658,519337175,false +2019,8,25,63091102,621215395,true +2019,8,25,385391910,88046086,false +2019,8,25,2069474222,2053068830,true +2019,8,25,2053729632,621241831,true +2019,8,26,580917050,1804213753,false +2019,8,26,33819061,118164769,false +2019,8,26,92966931,1101057259,false +2019,8,26,1624170332,24768902,false +2019,8,26,662821332,1146155590,true +2019,8,26,1243125124,873932133,false +2019,8,26,449269555,136598612,true +2019,8,26,1667792845,300658505,false +2019,8,26,1629055413,587165348,true +2019,8,26,1004432322,529502889,false +2019,8,27,1811089129,1535044365,true +2019,8,27,164067780,855522868,false +2019,8,27,21997233,2130524553,true +2019,8,27,675016031,892721843,true +2019,8,27,382452299,184031292,false +2019,8,27,995639828,1699732934,true +2019,8,27,1720196626,742986306,false +2019,8,27,1400973542,1667426784,true +2019,8,27,570599474,146245400,false +2019,8,27,764850261,409762121,true +2019,8,28,1869587653,1904475574,false +2019,8,28,1715243927,1250272278,true +2019,8,28,844014602,1248734657,true +2019,8,28,1735624476,1958646104,false +2019,8,28,1013684675,1032689240,true +2019,8,28,871860168,2109497836,true +2019,8,28,1757705432,1500085602,false +2019,8,28,1402226548,1997385649,true +2019,8,28,1132289219,176954221,true +2019,8,28,278735998,394896920,true +2019,8,29,1753960073,594958213,true +2019,8,29,2105502624,1718112573,true +2019,8,29,81249566,1408768896,true +2019,8,29,1315695176,1400018712,true +2019,8,29,438139266,701285558,false +2019,8,29,804021525,1186165342,false +2019,8,29,800761091,343725603,true +2019,8,29,2035220827,1976568101,true +2019,8,29,317234277,313305180,false +2019,8,29,1871706546,952248696,false +2019,8,30,1554687196,779333864,false +2019,8,30,311645428,1317086293,true +2019,8,30,1804704800,1386147285,false +2019,8,30,219864130,2064382915,true +2019,8,30,873071360,1907514271,false +2019,8,30,1180606442,1572926223,false +2019,8,30,194788925,79030992,true +2019,8,30,1845074997,1578686544,true +2019,8,30,2058421147,1116766162,true +2019,8,30,610223762,744383669,false +2019,8,31,2029975909,1122693871,true +2019,8,31,2096777370,1405478233,true +2019,8,31,1907039450,867374974,true +2019,8,31,1544962520,1715507340,false +2019,8,31,1803526198,35018909,false +2019,8,31,361587296,1256098765,true +2019,8,31,653273946,1167116051,true +2019,8,31,1680072603,666322159,false +2019,8,31,1221972550,2100975404,false +2019,8,31,1831139790,929656092,false +2019,9,1,2064595667,1728304814,true +2019,9,1,274945097,802827957,false +2019,9,1,1035755384,1932567092,false +2019,9,1,427692864,364457792,false +2019,9,1,2139795201,1356572309,true +2019,9,1,765483452,342303967,true +2019,9,1,59967870,1971164712,true +2019,9,1,262516516,1762478860,false +2019,9,1,2069616852,1450752756,false +2019,9,1,1083004756,2053792903,false +2019,9,2,406212527,1482376762,true +2019,9,2,1770787125,1620527289,true +2019,9,2,1928741848,1314110167,false +2019,9,2,2039992149,1522649349,true +2019,9,2,2021863692,1211753328,false +2019,9,2,372646646,223155887,true +2019,9,2,1869694951,706805352,true +2019,9,2,93064579,645811299,false +2019,9,2,1943605389,1765423367,false +2019,9,2,1582697842,844195473,false +2019,9,3,1635298061,911181930,true +2019,9,3,1290294012,763714564,false +2019,9,3,1108617137,919222792,true +2019,9,3,59336242,1864842380,false +2019,9,3,546004924,522946293,true +2019,9,3,1586166613,2111688080,false +2019,9,3,1680814918,499833898,false +2019,9,3,921471871,815668448,false +2019,9,3,2049026865,1043445620,true +2019,9,3,391010041,1544192035,false +2019,9,4,1815823988,1069661159,true +2019,9,4,1398803417,1235780527,false +2019,9,4,517095462,660752860,false +2019,9,4,1013462718,1358362038,false +2019,9,4,1776873489,718292450,false +2019,9,4,738604216,1247712006,false +2019,9,4,565713338,1675566895,true +2019,9,4,1396860105,1082683597,true +2019,9,4,1739988512,230515064,true +2019,9,4,1868833922,126559017,false +2019,9,5,544285341,135954225,false +2019,9,5,1681688228,1092641379,false +2019,9,5,272133035,2056116388,false +2019,9,5,242416589,1518969124,false +2019,9,5,72348800,1200660841,false +2019,9,5,1604265117,8598547,true +2019,9,5,1428931779,1379072280,false +2019,9,5,1410009559,1848600463,false +2019,9,5,1745435581,297248090,false +2019,9,5,574280290,1911077512,false +2019,9,6,1361677524,570305178,false +2019,9,6,1336676418,473660833,false +2019,9,6,1551862594,46061059,false +2019,9,6,721269978,599383079,true +2019,9,6,1265939247,1963685532,true +2019,9,6,1845164564,93579385,true +2019,9,6,731294915,1109349525,false +2019,9,6,1098178887,1441347578,false +2019,9,6,1633378770,1106525472,false +2019,9,6,1956553199,1890581160,false +2019,9,7,2104080672,539330164,true +2019,9,7,1609978266,1814747360,false +2019,9,7,747582074,1293325003,false +2019,9,7,364020621,1127435779,true +2019,9,7,1662878996,364634737,false +2019,9,7,810773013,1573994739,false +2019,9,7,2143360492,718603335,true +2019,9,7,1276081279,2016236455,true +2019,9,7,1192439329,102562117,false +2019,9,7,1353927719,1476679994,true +2019,9,8,1694914944,1487256286,false +2019,9,8,611303463,166569549,true +2019,9,8,1823790008,1944835409,false +2019,9,8,617575045,2057376145,false +2019,9,8,978351855,2027372116,true +2019,9,8,1395999475,1462078160,true +2019,9,8,1589168300,164040085,true +2019,9,8,1736725125,1284947157,true +2019,9,8,298949214,685527045,false +2019,9,8,1788174556,1969441532,false +2019,9,9,298174681,83634158,true +2019,9,9,1991966399,642348372,false +2019,9,9,1153698474,905419584,false +2019,9,9,2087187231,823416474,false +2019,9,9,1327300369,2127439509,true +2019,9,9,1633683175,396301684,false +2019,9,9,15917046,1703098083,true +2019,9,9,1426819386,593974969,true +2019,9,9,798287687,1493916884,true +2019,9,9,1101467877,2527394,false +2019,9,10,47625331,1578054711,false +2019,9,10,218274089,1793040190,true +2019,9,10,386029823,1823729419,false +2019,9,10,243394358,981448830,false +2019,9,10,1364802416,2054010638,false +2019,9,10,1280998114,1114614447,true +2019,9,10,325797366,410214883,true +2019,9,10,859109511,2097920581,false +2019,9,10,1134488226,1757041435,true +2019,9,10,14043919,650153020,false +2019,9,11,642316558,535674065,false +2019,9,11,1150452879,1362944127,false +2019,9,11,516825525,582855314,false +2019,9,11,1300041995,405308431,false +2019,9,11,16114044,2140685261,true +2019,9,11,903934510,886465769,false +2019,9,11,1609530387,1006471681,true +2019,9,11,1389085727,603660758,true +2019,9,11,1550136884,307051604,true +2019,9,11,352848580,1565744805,true +2019,9,12,653474036,563412334,false +2019,9,12,1772686334,2006032782,false +2019,9,12,1471217045,691646876,true +2019,9,12,1804867283,2042501281,false +2019,9,12,2044978598,567745201,true +2019,9,12,865196159,1021996689,false +2019,9,12,1160070482,594693143,false +2019,9,12,967645144,1254054981,true +2019,9,12,748074527,184712467,true +2019,9,12,1066085530,977991907,false +2019,9,13,1651873441,598036524,true +2019,9,13,646683495,534215777,false +2019,9,13,2147366849,170569256,false +2019,9,13,410433701,640108729,true +2019,9,13,318501543,1329930288,true +2019,9,13,8119174,734604373,false +2019,9,13,249842285,188217322,true +2019,9,13,498409424,1157037132,true +2019,9,13,176087842,2120736171,false +2019,9,13,1113808971,763391874,true +2019,9,14,1383893963,162880535,false +2019,9,14,633996510,1372560349,false +2019,9,14,809921368,2109081033,false +2019,9,14,1329313465,1718496498,true +2019,9,14,305701079,492285133,true +2019,9,14,1396390437,1792569352,true +2019,9,14,1836967233,1734026526,true +2019,9,14,1783746775,397095747,true +2019,9,14,2110523824,637010531,false +2019,9,14,1231401161,1916028493,false +2019,9,15,860034801,742456990,false +2019,9,15,512668434,1685829850,true +2019,9,15,949840797,510069387,false +2019,9,15,1086325557,689033180,true +2019,9,15,881921096,254722524,false +2019,9,15,2095256277,792634437,true +2019,9,15,1765768545,356904101,false +2019,9,15,1664857819,125011617,false +2019,9,15,179273949,250817667,true +2019,9,15,190704231,1169303792,true +2019,9,16,1077150145,473218918,false +2019,9,16,23826733,774653072,true +2019,9,16,3532327,1724494611,true +2019,9,16,1489077765,2130805773,false +2019,9,16,493845587,694537130,true +2019,9,16,453417947,734394674,true +2019,9,16,1531138542,1542458589,true +2019,9,16,1423033920,428205501,false +2019,9,16,1846840457,725620886,true +2019,9,16,9221198,1993091441,true +2019,9,17,1267392754,1478672192,false +2019,9,17,1901695897,197579396,false +2019,9,17,1642732286,577961202,true +2019,9,17,2071642725,1921263009,true +2019,9,17,1082022361,1102349349,true +2019,9,17,1279573851,1146390764,false +2019,9,17,879215872,1911534022,true +2019,9,17,98693743,1958289871,true +2019,9,17,1333206111,1718462393,false +2019,9,17,359058873,852572024,false +2019,9,18,1712861789,653074995,false +2019,9,18,479628905,640326734,true +2019,9,18,1971300537,227916139,false +2019,9,18,1675519272,1780747979,false +2019,9,18,46219635,1923445908,false +2019,9,18,801657465,966770302,false +2019,9,18,747522890,582691878,true +2019,9,18,1421369086,1151564826,false +2019,9,18,1971205491,354683093,false +2019,9,18,267344043,31630303,false +2019,9,19,1423685770,1161413071,true +2019,9,19,667202891,121771983,true +2019,9,19,671109877,1904076819,false +2019,9,19,2009987051,2066920279,true +2019,9,19,2026284339,459922945,true +2019,9,19,1840957944,1957958783,true +2019,9,19,1539217090,1366155637,true +2019,9,19,1662595650,727021249,false +2019,9,19,547858093,1795693576,false +2019,9,19,85050143,1163475471,false +2019,9,20,490566365,499699787,false +2019,9,20,996576853,2056363191,false +2019,9,20,1012328380,1577436188,false +2019,9,20,513079466,1220131106,true +2019,9,20,419675603,466995045,true +2019,9,20,1703376924,835719328,true +2019,9,20,506428776,1389362143,true +2019,9,20,1358671663,433218245,false +2019,9,20,1502232645,1787370933,true +2019,9,20,1472169183,1696290045,false +2019,9,21,1867671598,1282723353,true +2019,9,21,551984138,1552355422,true +2019,9,21,1538755764,1462802472,true +2019,9,21,1887712886,805145584,false +2019,9,21,1124961637,1361208755,true +2019,9,21,165473981,1352157138,true +2019,9,21,1854541196,1015688386,true +2019,9,21,888281538,689035790,true +2019,9,21,668546291,1737365489,false +2019,9,21,2054544261,296445942,true +2019,9,22,1622046067,46537464,false +2019,9,22,958159974,1572736870,true +2019,9,22,2073614129,756947444,true +2019,9,22,1259004733,1418914105,false +2019,9,22,640220627,1205359955,false +2019,9,22,571553227,1958068946,false +2019,9,22,1592936219,1249508720,true +2019,9,22,677008503,474906256,true +2019,9,22,1596727205,1996257346,true +2019,9,22,891207508,1742752637,true +2019,9,23,1960871100,296439978,false +2019,9,23,1672004056,1299260065,true +2019,9,23,704098945,564841023,true +2019,9,23,550574045,717178037,true +2019,9,23,6669274,1668873653,true +2019,9,23,2104715701,1358781747,false +2019,9,23,40345060,1018429735,true +2019,9,23,1435233914,1136887796,true +2019,9,23,1319793374,1214596334,true +2019,9,23,1877265132,144067798,true +2019,9,24,1693550785,789366995,false +2019,9,24,885444718,56281385,false +2019,9,24,1613581830,2146397877,true +2019,9,24,1480057605,559825103,false +2019,9,24,645215050,373376763,true +2019,9,24,1242879594,1831354700,false +2019,9,24,741001934,1303351405,true +2019,9,24,994955657,423751494,true +2019,9,24,1353934592,869537868,true +2019,9,24,2004716807,1427051297,false +2019,9,25,777021339,47448246,false +2019,9,25,388816034,1476498355,false +2019,9,25,448506105,137593543,true +2019,9,25,713438342,213210700,false +2019,9,25,1147841450,1253398250,false +2019,9,25,305074932,709329286,false +2019,9,25,929634930,1488116060,true +2019,9,25,400536518,489878678,true +2019,9,25,407036729,366940630,false +2019,9,25,1855063738,1573254934,false +2019,9,26,274412437,1599425736,true +2019,9,26,1144969863,1844954495,false +2019,9,26,1435755508,928006587,false +2019,9,26,2132827685,569130189,false +2019,9,26,603407500,175285677,true +2019,9,26,145870558,1406702596,true +2019,9,26,1964729762,1461244292,false +2019,9,26,1964061938,629496977,false +2019,9,26,388753607,1486201840,false +2019,9,26,1661911544,1810037426,false +2019,9,27,949467921,1393904457,true +2019,9,27,1094224714,1992359285,true +2019,9,27,1109907593,396820311,false +2019,9,27,1998407700,1666466284,false +2019,9,27,540538687,1839477764,true +2019,9,27,1753169375,1157705633,true +2019,9,27,540656825,784811362,false +2019,9,27,1042083771,320318585,true +2019,9,27,1155975882,1241864850,true +2019,9,27,244335428,1787704447,true +2019,9,28,1356802280,1835339882,true +2019,9,28,797504577,1069866578,false +2019,9,28,69641626,300201453,true +2019,9,28,1325148813,1145278248,false +2019,9,28,1759788958,1768182777,false +2019,9,28,984754049,695683498,true +2019,9,28,1043327181,916434601,true +2019,9,28,1979914024,1319627022,false +2019,9,28,10454250,1187953257,false +2019,9,28,1547529941,390319677,true +2019,9,29,1307063523,1851584429,false +2019,9,29,872461818,1189844715,true +2019,9,29,1980199722,1455706048,true +2019,9,29,2013687636,315225822,true +2019,9,29,677126187,1213471282,false +2019,9,29,1784473533,2067778025,true +2019,9,29,685860375,243201482,false +2019,9,29,110314832,1738703164,false +2019,9,29,1323491694,1340340481,true +2019,9,29,1411849370,1696250131,false +2019,9,30,1658114068,594697592,false +2019,9,30,275735311,712842229,true +2019,9,30,406698714,580062953,true +2019,9,30,811703554,1099648764,true +2019,9,30,654716344,338002297,true +2019,9,30,2024338120,1926989533,false +2019,9,30,974860593,1695521919,false +2019,9,30,364723154,1408837550,true +2019,9,30,994141081,2140266007,true +2019,9,30,764237793,583041424,true +2019,9,31,1772641075,234453274,false +2019,9,31,274541991,1456728789,false +2019,9,31,2045473372,333439572,true +2019,9,31,182365032,261960758,true +2019,9,31,663278920,1307663118,true +2019,9,31,795738495,1611091501,false +2019,9,31,1714400506,981423686,false +2019,9,31,1985302480,1233649303,true +2019,9,31,770188658,1381395240,true +2019,9,31,1261041297,80170025,true +2019,10,1,1164238165,1612559023,true +2019,10,1,227932142,798218591,false +2019,10,1,513645427,1988378475,true +2019,10,1,1502620526,900881125,true +2019,10,1,1203438331,740009440,false +2019,10,1,2132131123,1010283955,false +2019,10,1,667810107,92838153,false +2019,10,1,1775985241,1369288527,false +2019,10,1,1495439417,1243496912,false +2019,10,1,1559437610,693733108,true +2019,10,2,1465151381,661225892,false +2019,10,2,1508398819,115464337,false +2019,10,2,1078626660,711954296,false +2019,10,2,1020532277,318825171,true +2019,10,2,1153874705,1215407046,true +2019,10,2,242933963,1357660097,true +2019,10,2,516573796,676692850,true +2019,10,2,1100901252,1613849494,true +2019,10,2,1734241588,1641151134,false +2019,10,2,619470869,1182029772,false +2019,10,3,22132354,697667537,false +2019,10,3,245446742,1697986729,true +2019,10,3,884190687,1350382845,false +2019,10,3,1938108009,1681492384,false +2019,10,3,1934141043,356929347,false +2019,10,3,247669364,1207210344,true +2019,10,3,792985854,2092066705,false +2019,10,3,852890031,1960913050,true +2019,10,3,1558472868,2065171814,true +2019,10,3,952277995,941018667,false +2019,10,4,209312882,964291169,true +2019,10,4,1192618984,1621752623,true +2019,10,4,1738639304,882897000,false +2019,10,4,1925870601,2144773012,false +2019,10,4,294656576,312507785,false +2019,10,4,34808911,1400185822,true +2019,10,4,964987809,1899079650,true +2019,10,4,785274779,1911200789,true +2019,10,4,1098814202,1891766582,false +2019,10,4,99583408,1431991893,false +2019,10,5,902078475,2007827702,false +2019,10,5,764419173,1390912453,true +2019,10,5,1281059950,553045221,false +2019,10,5,333483528,1172341987,false +2019,10,5,802115150,1906200487,false +2019,10,5,1125643234,1263766116,false +2019,10,5,1183246479,401748440,true +2019,10,5,1775070046,205451806,false +2019,10,5,1579988133,2103553400,true +2019,10,5,1410261310,1501145004,true +2019,10,6,869122209,1109226713,false +2019,10,6,2065637857,873190101,true +2019,10,6,200271883,1122635081,true +2019,10,6,1124863633,1899761115,false +2019,10,6,615536808,44788531,false +2019,10,6,1705940803,1436232760,true +2019,10,6,1666149445,1167527023,true +2019,10,6,1992482141,986707582,false +2019,10,6,174806802,773170925,true +2019,10,6,1487896405,1617549895,true +2019,10,7,2029907509,376213778,false +2019,10,7,827903562,433345920,false +2019,10,7,360213667,1881120330,false +2019,10,7,168191460,1013672568,false +2019,10,7,1881386078,37885387,true +2019,10,7,1698667552,717078130,true +2019,10,7,1245993122,631634558,false +2019,10,7,1541624275,1631368877,true +2019,10,7,11352499,6218365,false +2019,10,7,805381638,929761309,false +2019,10,8,1695118658,1449486770,false +2019,10,8,33668474,1750289493,true +2019,10,8,1732544165,1555279476,false +2019,10,8,1723335929,857365062,false +2019,10,8,1616664659,499486779,true +2019,10,8,282189886,1032055777,false +2019,10,8,1173707884,918120381,true +2019,10,8,1169746064,874942913,false +2019,10,8,1044534314,47314198,true +2019,10,8,709789041,803777642,true +2019,10,9,1618392724,137370566,false +2019,10,9,343283439,623978169,true +2019,10,9,568321230,1780745639,false +2019,10,9,1906175085,411668384,true +2019,10,9,1972807372,248003894,true +2019,10,9,2066197513,971309374,true +2019,10,9,1949259108,2010549427,false +2019,10,9,152268268,879792630,true +2019,10,9,2056695532,1986773774,false +2019,10,9,276853786,865265680,true +2019,10,10,553527773,1189886108,false +2019,10,10,1392173738,1497662220,false +2019,10,10,1403174213,1218698497,true +2019,10,10,1441824100,2043180459,true +2019,10,10,1726617822,1310723292,true +2019,10,10,772147005,1315189341,true +2019,10,10,540358445,1975204112,false +2019,10,10,401325353,820952980,true +2019,10,10,2105319920,2065685188,false +2019,10,10,1602181428,1426439209,false +2019,10,11,292470217,1190925930,true +2019,10,11,2023515114,792075234,false +2019,10,11,1142328298,1945509237,true +2019,10,11,952969583,408158886,false +2019,10,11,1835571486,1812242819,false +2019,10,11,1838529758,1155051792,true +2019,10,11,1532680091,1579799839,true +2019,10,11,1754455999,925278051,true +2019,10,11,865400147,1491293555,false +2019,10,11,667268277,978172930,false +2019,10,12,494072507,1930968442,true +2019,10,12,1931324350,67033340,true +2019,10,12,1301831241,1153457809,false +2019,10,12,1330144143,329496661,false +2019,10,12,1344026388,1418034050,true +2019,10,12,1594171121,699296005,true +2019,10,12,1139549534,982006352,false +2019,10,12,639776311,639620844,false +2019,10,12,573637101,1788255863,false +2019,10,12,322923548,464723504,false +2019,10,13,675913085,60231770,false +2019,10,13,344101541,1521839800,false +2019,10,13,1435365304,1486393364,false +2019,10,13,1218201573,623158977,true +2019,10,13,1469733226,1762361579,false +2019,10,13,1112150963,1059607495,false +2019,10,13,839780348,660918286,true +2019,10,13,1525196460,1615859508,false +2019,10,13,204593229,1080747120,true +2019,10,13,996456478,1593393296,true +2019,10,14,656336260,1579030225,true +2019,10,14,53007273,1208402220,false +2019,10,14,363416701,1423980640,false +2019,10,14,307863744,114539901,false +2019,10,14,56633033,1000618871,false +2019,10,14,1570610504,444403359,true +2019,10,14,940992654,484062554,false +2019,10,14,92224012,1062810298,true +2019,10,14,1562358430,1337981193,true +2019,10,14,418157250,680957249,true +2019,10,15,1724014237,1701071488,false +2019,10,15,852225227,1360334910,true +2019,10,15,797875483,568618746,false +2019,10,15,1224904445,1944913454,false +2019,10,15,691961307,109672979,true +2019,10,15,1535160853,1231173655,false +2019,10,15,528529612,264075488,false +2019,10,15,1432554661,1039670747,true +2019,10,15,1257773657,530535248,false +2019,10,15,1865164435,827463419,false +2019,10,16,732635162,1591163253,false +2019,10,16,281935755,1136908351,false +2019,10,16,2004724499,1422034314,true +2019,10,16,2080504052,1383733003,false +2019,10,16,1743012601,1169978574,false +2019,10,16,750333758,1070869822,true +2019,10,16,2126727743,425052297,true +2019,10,16,75222957,204232938,false +2019,10,16,2099397844,825660890,true +2019,10,16,1598639848,661905750,false +2019,10,17,1058866135,53941794,false +2019,10,17,2129527285,1414323966,true +2019,10,17,18527925,1562606366,true +2019,10,17,1669484139,905328269,true +2019,10,17,479829605,819884311,true +2019,10,17,778813440,736925446,true +2019,10,17,1555626425,886728367,true +2019,10,17,579707068,1202293146,false +2019,10,17,690539022,1995748657,false +2019,10,17,1950582395,525856221,true +2019,10,18,1079956916,1754967409,false +2019,10,18,930640767,1671112531,true +2019,10,18,1153688034,1231563932,true +2019,10,18,1774582088,362207630,true +2019,10,18,1951207457,1799094671,true +2019,10,18,2112558052,119203415,true +2019,10,18,1256762211,1574430339,true +2019,10,18,1512997243,1656259992,false +2019,10,18,1525157708,680621114,false +2019,10,18,357956802,1147621628,true +2019,10,19,156876884,178129645,true +2019,10,19,1202080405,351079811,false +2019,10,19,1980683715,1625655974,true +2019,10,19,1311263218,1330735996,false +2019,10,19,361270980,1410115543,true +2019,10,19,1404533504,212070820,false +2019,10,19,950591994,870777409,true +2019,10,19,722898158,1933140571,false +2019,10,19,1753815898,1507308434,true +2019,10,19,1449116619,98972204,true +2019,10,20,1982447165,613097526,false +2019,10,20,1317551785,1036234012,true +2019,10,20,1731719651,241844318,true +2019,10,20,1854317479,590298232,false +2019,10,20,995052764,991143971,false +2019,10,20,1630358193,223375652,false +2019,10,20,59699225,1917066568,true +2019,10,20,103610657,1247811220,true +2019,10,20,840047761,2043401751,false +2019,10,20,1136947075,433402094,false +2019,10,21,2135283382,1404592193,false +2019,10,21,1668857890,692876561,false +2019,10,21,517820725,259061711,false +2019,10,21,2021552175,1987777393,false +2019,10,21,2097663161,683447680,false +2019,10,21,151590565,454141705,false +2019,10,21,1257106596,517505328,false +2019,10,21,1634292525,982667215,false +2019,10,21,520044792,304844826,false +2019,10,21,319022263,1881375856,true +2019,10,22,1601143790,25081864,true +2019,10,22,908733055,1296402751,false +2019,10,22,2105398760,120461279,false +2019,10,22,2119156660,2063846045,true +2019,10,22,1791929536,561926913,true +2019,10,22,1698898184,1497245501,true +2019,10,22,22430632,1504112759,true +2019,10,22,195690688,1496093464,false +2019,10,22,1845997329,1245882343,true +2019,10,22,695650603,1616705008,true +2019,10,23,1230658644,46593470,false +2019,10,23,1140971140,85381286,true +2019,10,23,1454524177,491996444,false +2019,10,23,600369320,1007196892,false +2019,10,23,194485896,1989232989,true +2019,10,23,771177003,162659987,true +2019,10,23,1953596239,220271192,true +2019,10,23,862469555,278768073,false +2019,10,23,396427826,525782441,true +2019,10,23,147642269,324693568,false +2019,10,24,293669673,1375884453,true +2019,10,24,2008283478,713188851,true +2019,10,24,699722653,1414228417,true +2019,10,24,2127173882,1688198893,false +2019,10,24,1989630329,281817498,true +2019,10,24,181594889,1031961459,false +2019,10,24,1964667067,1502532323,true +2019,10,24,859002201,1614695368,false +2019,10,24,1853798270,1307938263,true +2019,10,24,909220240,1000684782,true +2019,10,25,820599987,831903502,false +2019,10,25,1843626690,1271340086,false +2019,10,25,1172112393,388522690,false +2019,10,25,49792276,602616188,true +2019,10,25,1943313836,823157982,false +2019,10,25,1668548458,971055712,true +2019,10,25,1139053289,1859905408,false +2019,10,25,1885146757,1132855004,true +2019,10,25,1673464024,208915620,true +2019,10,25,1140622894,771363842,true +2019,10,26,2035724440,1027811638,false +2019,10,26,1576517680,2037382102,false +2019,10,26,857645252,578287301,false +2019,10,26,1057866322,805595504,false +2019,10,26,1486525991,919137922,true +2019,10,26,1777333148,1963390704,false +2019,10,26,1253755358,2119797590,false +2019,10,26,283113083,1135136490,true +2019,10,26,1945229671,96141876,false +2019,10,26,1873580202,146275668,true +2019,10,27,1885739336,1355958005,true +2019,10,27,1442724586,1298830369,true +2019,10,27,709207385,1777090898,false +2019,10,27,1146544620,840969729,true +2019,10,27,87337436,213121257,true +2019,10,27,1329977083,266520074,true +2019,10,27,1877362393,557189195,false +2019,10,27,1219803718,629728562,true +2019,10,27,1302078336,86632061,false +2019,10,27,1259968990,1839587042,false +2019,10,28,1126226310,371121018,true +2019,10,28,1821927363,60017676,true +2019,10,28,2116214956,890736537,false +2019,10,28,158242452,1206139010,false +2019,10,28,218439924,835020511,true +2019,10,28,213970874,2038798476,true +2019,10,28,1309138489,169545891,false +2019,10,28,2048450349,412986597,true +2019,10,28,156752406,180629785,false +2019,10,28,1639731234,1193576879,false +2019,10,29,282943852,933535126,false +2019,10,29,381426496,664080632,false +2019,10,29,2109477094,659791272,false +2019,10,29,2054210098,1825696739,true +2019,10,29,917274691,1083953760,false +2019,10,29,524096939,2102424993,false +2019,10,29,1004029526,1690114656,true +2019,10,29,1755396669,1369715118,false +2019,10,29,572520447,1433326721,true +2019,10,29,264674701,1973377810,false +2019,10,30,618820220,1820038193,true +2019,10,30,105685197,1209101469,true +2019,10,30,61406706,442925537,false +2019,10,30,94721665,764953168,true +2019,10,30,852504313,661294870,false +2019,10,30,963543294,141570672,false +2019,10,30,30471818,1677076722,false +2019,10,30,1403737117,235096319,true +2019,10,30,1151767979,1818656090,true +2019,10,30,1034627100,527907460,true +2019,10,31,3620511,1840547334,true +2019,10,31,2058722474,485997424,true +2019,10,31,1951375252,1761489427,true +2019,10,31,571503232,1412579201,true +2019,10,31,307393274,973013491,false +2019,10,31,1855123237,911132909,false +2019,10,31,1160843273,59673304,false +2019,10,31,1905246632,551350597,false +2019,10,31,1127277094,1429695973,true +2019,10,31,908879990,481674820,false +2019,11,1,1069758495,1570155718,false +2019,11,1,7133078,1157301008,false +2019,11,1,845419643,358070403,false +2019,11,1,1588454383,1121888914,false +2019,11,1,399777363,780431538,false +2019,11,1,1825608555,18260465,true +2019,11,1,1581225576,108943430,true +2019,11,1,264581897,533574226,false +2019,11,1,1564333688,166058672,false +2019,11,1,1446890308,1309466480,true +2019,11,2,1054568704,744056598,false +2019,11,2,438601818,818893990,false +2019,11,2,1373443558,1125497029,false +2019,11,2,873578324,2130304125,true +2019,11,2,1914633416,943041319,false +2019,11,2,1015394778,2038036196,true +2019,11,2,1070026721,459195624,false +2019,11,2,264299074,636272793,false +2019,11,2,834206400,691144978,true +2019,11,2,1438187095,1283656293,true +2019,11,3,1214113352,829548095,false +2019,11,3,23327429,1154337091,false +2019,11,3,378937264,1492065079,true +2019,11,3,1729358390,157253770,false +2019,11,3,1680226944,1361669528,true +2019,11,3,1705550462,361692581,true +2019,11,3,1818197429,1346759251,true +2019,11,3,1629024108,1222885647,true +2019,11,3,1015327391,875325733,true +2019,11,3,1486112278,1528451712,false +2019,11,4,420378636,1986003805,false +2019,11,4,1202918589,1621069460,true +2019,11,4,1205520092,106589053,true +2019,11,4,167660185,1471564373,false +2019,11,4,452209950,238446246,false +2019,11,4,2065504307,1338410345,true +2019,11,4,106384182,2000249295,true +2019,11,4,880503826,1644977622,false +2019,11,4,15631052,1279527984,true +2019,11,4,1774538978,1532371098,true +2019,11,5,1581295972,1261196649,false +2019,11,5,249686375,400210061,true +2019,11,5,1462583548,492566862,false +2019,11,5,1165272026,269906236,false +2019,11,5,568930046,390280297,false +2019,11,5,1759129975,1625626058,true +2019,11,5,220063332,22160254,false +2019,11,5,233842797,1917514742,false +2019,11,5,202249194,272170434,true +2019,11,5,760661362,640600530,false +2019,11,6,1091904406,1572969604,false +2019,11,6,2088392611,581775571,false +2019,11,6,291568905,1811344498,false +2019,11,6,1737575545,757744453,true +2019,11,6,2052663373,535937991,false +2019,11,6,289530618,685445447,true +2019,11,6,1806024482,871398914,false +2019,11,6,1099568246,345542865,false +2019,11,6,747122423,1575975153,true +2019,11,6,1295093347,1258231935,true +2019,11,7,744779278,1495625790,true +2019,11,7,591559741,898479893,true +2019,11,7,1446703747,1470008257,true +2019,11,7,1928702329,1935731182,true +2019,11,7,1861354533,1204556342,false +2019,11,7,2034369252,649388760,true +2019,11,7,1114831468,1846732084,false +2019,11,7,279512068,486590997,true +2019,11,7,868232681,1675068534,false +2019,11,7,1286142048,1589756780,false +2019,11,8,1864427782,187180501,false +2019,11,8,2075425199,1117848978,true +2019,11,8,1572799986,536528002,false +2019,11,8,1348668257,1295685980,true +2019,11,8,219625481,1783739744,false +2019,11,8,1497460997,1844210284,true +2019,11,8,1449250074,28483477,false +2019,11,8,590819439,447792477,true +2019,11,8,357699420,524058526,false +2019,11,8,712042929,151076766,false +2019,11,9,835316458,897968282,false +2019,11,9,247045701,1263788916,true +2019,11,9,1457332771,878107261,true +2019,11,9,462812663,701261538,true +2019,11,9,1123664452,172789098,false +2019,11,9,80206702,1959640676,true +2019,11,9,1577893387,1676300655,false +2019,11,9,1681636206,244356287,false +2019,11,9,1811621458,271279944,false +2019,11,9,158734623,583247707,true +2019,11,10,192379093,392581637,true +2019,11,10,1376016128,871484118,true +2019,11,10,1742437294,1531699867,true +2019,11,10,1399871316,2020867549,true +2019,11,10,1835149346,1393161676,true +2019,11,10,1974419492,1092976745,true +2019,11,10,813814227,1382799851,false +2019,11,10,1913196078,2045572080,true +2019,11,10,1801457534,226911591,true +2019,11,10,1192429402,1555834157,false +2019,11,11,552698051,561201182,false +2019,11,11,734156939,411818633,false +2019,11,11,2013622447,884642029,true +2019,11,11,1037011943,1578691154,true +2019,11,11,1103501050,529167860,true +2019,11,11,2068700135,1561494071,false +2019,11,11,1509091903,260848005,false +2019,11,11,942867667,861470159,false +2019,11,11,1239131028,1375037120,true +2019,11,11,1091234163,434797583,false +2019,11,12,846483528,174164032,false +2019,11,12,1534949664,1533256691,true +2019,11,12,1416145082,1753008233,true +2019,11,12,355570804,40126167,true +2019,11,12,1588456320,1159310903,false +2019,11,12,1067156300,241228585,false +2019,11,12,2022523475,816847521,true +2019,11,12,140294622,1948431479,false +2019,11,12,1059015301,1742359014,true +2019,11,12,1876589577,1445230092,false +2019,11,13,818236648,973619556,false +2019,11,13,1849963825,380703644,true +2019,11,13,442175850,215649736,true +2019,11,13,1765632060,309843883,false +2019,11,13,184654039,1227609779,false +2019,11,13,645119635,649914798,false +2019,11,13,1635218305,566518835,true +2019,11,13,2033927732,81051166,true +2019,11,13,682522044,1804308370,true +2019,11,13,1373777455,248662261,false +2019,11,14,1898660715,1888423029,false +2019,11,14,67933740,1113048906,true +2019,11,14,819733020,25055297,false +2019,11,14,1472209101,587992839,false +2019,11,14,1208468831,1864232240,true +2019,11,14,1938242052,1001495537,true +2019,11,14,307751765,187812791,false +2019,11,14,1491351987,136812405,true +2019,11,14,50835383,733059060,false +2019,11,14,639436859,1072295165,true +2019,11,15,806029762,1735248484,true +2019,11,15,1160150459,622177974,true +2019,11,15,603981249,1268267547,true +2019,11,15,1277341232,2048896782,false +2019,11,15,1000768819,428700711,false +2019,11,15,973308854,1756942676,false +2019,11,15,1000323268,1992685289,true +2019,11,15,2001845658,1515601985,true +2019,11,15,1371647372,158546317,true +2019,11,15,1799583232,675480405,true +2019,11,16,14105424,257222372,false +2019,11,16,1354891589,940532804,false +2019,11,16,593163196,333002828,false +2019,11,16,1449140580,768855021,true +2019,11,16,1274639051,1285535856,false +2019,11,16,731608335,1361986199,false +2019,11,16,727789281,377042688,true +2019,11,16,1698729267,248586594,false +2019,11,16,1659614010,1341730224,true +2019,11,16,298024226,1118294821,true +2019,11,17,936221113,1901892645,true +2019,11,17,1708644511,775704895,true +2019,11,17,1066053964,1512960921,false +2019,11,17,587544691,1494836483,false +2019,11,17,1428493389,544588885,true +2019,11,17,260594014,355872957,true +2019,11,17,541497005,910528087,false +2019,11,17,971901564,1740516231,true +2019,11,17,1697740249,364002242,true +2019,11,17,1713059708,54796654,false +2019,11,18,388015241,1016747749,true +2019,11,18,1795995702,1789459853,true +2019,11,18,380238590,926736119,false +2019,11,18,300417664,1156691500,false +2019,11,18,719917150,44025319,true +2019,11,18,515028058,2011149062,true +2019,11,18,1603542059,1324242635,false +2019,11,18,649001923,1382089793,true +2019,11,18,1679558408,482969548,false +2019,11,18,1510242008,943312066,true +2019,11,19,530864201,573170599,true +2019,11,19,1944850957,1283321048,true +2019,11,19,541655559,37557793,true +2019,11,19,1162804120,1190417023,true +2019,11,19,1326497702,1138920441,true +2019,11,19,2068302449,1326901924,true +2019,11,19,1187502867,445305400,true +2019,11,19,847103088,1409880882,false +2019,11,19,157109028,1216201208,false +2019,11,19,1770426987,1817718735,false +2019,11,20,1709394402,903535866,true +2019,11,20,1248721510,1270854549,false +2019,11,20,1458326435,1006492826,true +2019,11,20,1160656695,55320614,false +2019,11,20,1926281273,1426401288,true +2019,11,20,704154151,333657841,false +2019,11,20,466320812,1502609362,false +2019,11,20,156008052,190215043,false +2019,11,20,1728682033,1647541273,false +2019,11,20,52844488,1321659949,true +2019,11,21,1519422815,1736855567,true +2019,11,21,597236731,979209338,false +2019,11,21,1965205708,825192592,true +2019,11,21,1398203836,470939928,true +2019,11,21,660477599,2133888242,true +2019,11,21,416841977,2126499655,false +2019,11,21,743560376,832409693,true +2019,11,21,1332939542,1096861172,false +2019,11,21,1704750460,1143631695,true +2019,11,21,841760852,1367934200,true +2019,11,22,1723045731,1247946774,false +2019,11,22,1255314688,795884355,true +2019,11,22,852474593,1440245350,false +2019,11,22,253822012,618534615,false +2019,11,22,21828672,1971550550,false +2019,11,22,1870983035,1442215894,true +2019,11,22,914307454,1730711651,true +2019,11,22,1275678991,780381723,true +2019,11,22,19544,1011896149,true +2019,11,22,376582489,1858688275,true +2019,11,23,1117232889,260508180,false +2019,11,23,1473220979,452887503,false +2019,11,23,829169172,1973498057,false +2019,11,23,1049307000,1253775238,true +2019,11,23,492109136,1535767018,false +2019,11,23,1361127343,861039874,false +2019,11,23,557972297,821406460,false +2019,11,23,2067544896,367655339,true +2019,11,23,500082834,124434994,true +2019,11,23,1172799561,437743408,false +2019,11,24,288850309,305558063,false +2019,11,24,2127581280,140475643,false +2019,11,24,721022143,1613735572,false +2019,11,24,1790140902,884631793,false +2019,11,24,1537368217,1346917166,true +2019,11,24,212202949,17345845,false +2019,11,24,715038717,632393933,true +2019,11,24,2135553631,1602820833,false +2019,11,24,222378855,1302016564,false +2019,11,24,1682605531,1049149935,false +2019,11,25,243981564,649023657,false +2019,11,25,1126844703,1054842063,true +2019,11,25,1475636974,1522242305,false +2019,11,25,2029785608,1500236381,false +2019,11,25,1251045803,1143038788,false +2019,11,25,2112173765,2041547562,true +2019,11,25,1417014163,417621562,true +2019,11,25,1140602214,135221088,true +2019,11,25,2043203101,1094561154,true +2019,11,25,348195319,1374124200,true +2019,11,26,532122802,1524766721,false +2019,11,26,180562624,879957970,false +2019,11,26,89554870,1297380812,false +2019,11,26,245186947,1513844793,true +2019,11,26,1739823718,2122783701,true +2019,11,26,1069793743,1498184465,true +2019,11,26,1997898906,858059020,false +2019,11,26,1503804349,917869355,false +2019,11,26,1298882965,1975349676,false +2019,11,26,72224694,879422922,true +2019,11,27,1322517335,297336556,false +2019,11,27,2033959701,847781790,true +2019,11,27,1691094590,2099765327,true +2019,11,27,1870132924,184333576,false +2019,11,27,1174352594,344256842,true +2019,11,27,1198307319,437807915,true +2019,11,27,24785927,1955127542,false +2019,11,27,1611129160,1456401277,true +2019,11,27,1268416111,1186036479,true +2019,11,27,938824680,1482989009,true +2019,11,28,1196994818,335837220,false +2019,11,28,1768901667,1014777104,false +2019,11,28,361837189,1868243410,false +2019,11,28,390906334,1123825952,true +2019,11,28,692049600,772634565,false +2019,11,28,489683945,266484903,true +2019,11,28,621821185,1433653175,false +2019,11,28,1481770976,1141225385,true +2019,11,28,369493401,698738309,true +2019,11,28,634386896,140810294,false +2019,11,29,1306512985,1762680653,true +2019,11,29,1796701663,1987694757,false +2019,11,29,770909754,2026735731,false +2019,11,29,369472414,532901641,false +2019,11,29,1156245172,142950295,true +2019,11,29,1250381268,571943908,false +2019,11,29,121417072,348701911,false +2019,11,29,155638501,516955726,true +2019,11,29,210049880,1896516346,false +2019,11,29,1729542427,1926730233,true +2019,11,30,24647220,1251304785,true +2019,11,30,1772216192,828495846,true +2019,11,30,768417876,1206988151,true +2019,11,30,126951182,1998709924,false +2019,11,30,340655607,648669747,false +2019,11,30,1828921683,1221244781,false +2019,11,30,33874542,167769641,true +2019,11,30,1467154662,1490185089,false +2019,11,30,929058607,490696736,true +2019,11,30,1224488255,1341819272,false +2019,11,31,488521039,1838723074,false +2019,11,31,295206606,1807565039,false +2019,11,31,915023394,623207142,false +2019,11,31,991222116,548712814,false +2019,11,31,5085678,1493867970,false +2019,11,31,1869220425,1623518018,true +2019,11,31,1755339762,465140120,true +2019,11,31,1943696539,335730874,false +2019,11,31,812777786,1278801209,false +2019,11,31,1702185414,188837420,true +2020,1,1,635823082,710260720,true +2020,1,1,797719859,1805538672,true +2020,1,1,1154248460,1924740542,false +2020,1,1,1606379905,1468162153,false +2020,1,1,146761480,2107207908,false +2020,1,1,1739023083,346452557,true +2020,1,1,1863741772,1292269039,false +2020,1,1,2136561053,150000042,true +2020,1,1,1695926731,1587644039,true +2020,1,1,2146949168,2011301204,true +2020,1,2,1523212129,1671169194,true +2020,1,2,689281408,352062661,true +2020,1,2,327649343,1389323042,true +2020,1,2,795842200,792658070,false +2020,1,2,1453176287,150665548,true +2020,1,2,401793134,30310939,true +2020,1,2,860165190,2036688787,true +2020,1,2,337607326,897771842,false +2020,1,2,1560830573,789286697,false +2020,1,2,773673833,1829135945,true +2020,1,3,310157864,1413663461,true +2020,1,3,1770132128,309027053,true +2020,1,3,1601833972,2083809356,true +2020,1,3,982798408,731369746,false +2020,1,3,533598046,1671964465,false +2020,1,3,471465239,1695952162,true +2020,1,3,1607508750,1948717937,true +2020,1,3,550057495,13920397,false +2020,1,3,1772081052,426323892,true +2020,1,3,1953673750,783051053,false +2020,1,4,1560397486,1661104448,false +2020,1,4,1211056814,1939268747,true +2020,1,4,2023361565,1065831474,false +2020,1,4,689689605,977063461,false +2020,1,4,1727187212,373871819,true +2020,1,4,1962130837,1442772556,true +2020,1,4,591749427,1445205387,false +2020,1,4,1249665950,984431929,false +2020,1,4,577855700,2038496074,false +2020,1,4,1714482199,977559650,false +2020,1,5,537847311,1984556955,true +2020,1,5,2140959529,653523004,true +2020,1,5,2103530127,728643349,true +2020,1,5,170232484,774785765,true +2020,1,5,318991053,2138324055,true +2020,1,5,1602303220,1570583667,true +2020,1,5,1958846034,937257443,true +2020,1,5,999147373,451813337,true +2020,1,5,61872323,1179116581,true +2020,1,5,581992726,893514654,true +2020,1,6,1315306917,273787458,true +2020,1,6,1030355514,1220246492,true +2020,1,6,536797451,1758474108,false +2020,1,6,1529228900,1312902980,true +2020,1,6,124239286,1050980438,false +2020,1,6,965383567,148438804,true +2020,1,6,1893295439,420687293,true +2020,1,6,431413940,1560650308,true +2020,1,6,44253631,1463051734,true +2020,1,6,190844160,2111834713,true +2020,1,7,1461536469,388769105,false +2020,1,7,1170800209,1235434649,true +2020,1,7,274053557,2008560964,false +2020,1,7,17870033,2106832030,false +2020,1,7,1631127499,831637763,false +2020,1,7,716596927,602836334,true +2020,1,7,492362035,1402336365,false +2020,1,7,1509269201,21595301,true +2020,1,7,232843988,704720544,true +2020,1,7,877491377,813952867,true +2020,1,8,1708169894,2043119137,false +2020,1,8,1083938936,2121317092,false +2020,1,8,217021651,16106189,false +2020,1,8,889895239,233014045,false +2020,1,8,681663161,1247252275,true +2020,1,8,1752886848,271005618,true +2020,1,8,1593160951,1440855592,false +2020,1,8,738250381,1593295892,false +2020,1,8,385013763,2104285876,true +2020,1,8,705562689,1633503671,false +2020,1,9,477812487,683777955,false +2020,1,9,20809521,1952301977,false +2020,1,9,698278640,1258899819,false +2020,1,9,1140618257,1560801427,false +2020,1,9,565478384,2137608221,true +2020,1,9,1561383299,1842492966,false +2020,1,9,99666413,743073944,false +2020,1,9,1526468570,1938513868,false +2020,1,9,953394317,1598493506,false +2020,1,9,1009445298,1132379883,true +2020,1,10,399298008,1818168299,false +2020,1,10,1964051132,840814077,true +2020,1,10,774052534,278256037,true +2020,1,10,1867822918,29828502,true +2020,1,10,963979943,1137084095,true +2020,1,10,1161344613,1990515774,true +2020,1,10,1952539787,323880988,false +2020,1,10,1825468889,1541825122,false +2020,1,10,1827438679,319512732,false +2020,1,10,1804232175,12460494,false +2020,1,11,1194761730,634906715,false +2020,1,11,1919281434,409447523,true +2020,1,11,1621862424,1849991404,true +2020,1,11,1354028539,1400743944,true +2020,1,11,464293040,1255274196,true +2020,1,11,89590788,1888482832,false +2020,1,11,1430471095,259919356,false +2020,1,11,1941297675,1798529669,false +2020,1,11,1700700628,728271310,false +2020,1,11,256281435,1734378514,false +2020,1,12,1674544460,1459157814,true +2020,1,12,318589732,1669001101,true +2020,1,12,1760829098,1342410180,false +2020,1,12,962573159,674990668,true +2020,1,12,304031013,1011122845,true +2020,1,12,1075540900,1144466628,true +2020,1,12,1402914542,1991398145,true +2020,1,12,1595136245,1587542827,true +2020,1,12,1364478961,1437943178,true +2020,1,12,333870177,1277520914,false +2020,1,13,1510788527,1325181049,true +2020,1,13,74118539,304739632,false +2020,1,13,1121518385,1544163494,false +2020,1,13,1240030980,95048915,false +2020,1,13,566028374,1390133226,true +2020,1,13,1199722219,843676347,true +2020,1,13,100568540,558628648,false +2020,1,13,707985828,446479681,false +2020,1,13,70235126,232036148,false +2020,1,13,1157580235,506552346,false +2020,1,14,46760786,1131805007,false +2020,1,14,2038013863,1583417374,true +2020,1,14,1447680214,1522590453,false +2020,1,14,1931585490,2107415506,true +2020,1,14,344110724,1080601122,true +2020,1,14,105124821,383898278,false +2020,1,14,899586109,1745008233,true +2020,1,14,33693910,1081107757,true +2020,1,14,484914529,1617250407,true +2020,1,14,799020989,756612788,true +2020,1,15,193638525,1441424512,false +2020,1,15,1755752277,339070212,false +2020,1,15,796912772,1912262370,false +2020,1,15,69250125,690906420,true +2020,1,15,21218115,758994497,false +2020,1,15,1496318002,958774479,true +2020,1,15,244507,1832812496,false +2020,1,15,1739513773,217038025,true +2020,1,15,982034974,558879327,true +2020,1,15,1773712817,1934526442,true +2020,1,16,787284763,2140262308,false +2020,1,16,1499622493,634355902,true +2020,1,16,1957913933,1933767193,true +2020,1,16,919543221,561150762,false +2020,1,16,551420322,341644032,true +2020,1,16,1096740239,2112807986,true +2020,1,16,1741525198,2012430168,false +2020,1,16,520178489,2028148385,true +2020,1,16,944602029,1564969974,true +2020,1,16,651535558,1715019136,true +2020,1,17,892636068,1362868227,false +2020,1,17,1341851404,1577901485,false +2020,1,17,293571323,1959965312,true +2020,1,17,1359678639,847549933,true +2020,1,17,764591464,849621669,true +2020,1,17,746269649,316926280,false +2020,1,17,390371137,605541437,false +2020,1,17,1624945984,1078512022,true +2020,1,17,347579923,1520525379,false +2020,1,17,1780941110,1898555531,true +2020,1,18,1201325967,1593599854,true +2020,1,18,468395279,553509785,false +2020,1,18,1063822346,1366812535,true +2020,1,18,373256820,2094562179,false +2020,1,18,1918662658,1422044465,true +2020,1,18,1691794421,787309186,false +2020,1,18,1541446763,115037393,false +2020,1,18,1135950856,1428296761,false +2020,1,18,1440776108,1017741052,false +2020,1,18,624808675,643387640,true +2020,1,19,2073883031,1422187631,true +2020,1,19,1666084017,1587137020,true +2020,1,19,1041715905,1788306269,false +2020,1,19,1833014533,1504304809,false +2020,1,19,176845824,33368283,true +2020,1,19,1925240233,295455474,true +2020,1,19,1048726787,1842497009,true +2020,1,19,1754156565,1309107571,false +2020,1,19,1860516149,380613413,false +2020,1,19,689063463,856315871,false +2020,1,20,513847408,382103202,true +2020,1,20,1763650049,447293140,true +2020,1,20,745178412,710238274,true +2020,1,20,1215868437,490501684,false +2020,1,20,2136482858,1880921125,true +2020,1,20,210018753,812600724,true +2020,1,20,1460941263,2039963556,false +2020,1,20,530154511,1240581212,true +2020,1,20,1320468390,102245811,true +2020,1,20,116527475,760807344,true +2020,1,21,330907849,1035634458,false +2020,1,21,950602396,1445223490,true +2020,1,21,173993497,1823221767,true +2020,1,21,15610808,1347259531,false +2020,1,21,836731794,1525589993,false +2020,1,21,197611697,2086793255,false +2020,1,21,546551328,1411793305,false +2020,1,21,108363347,1371669328,true +2020,1,21,1476822457,1891932460,false +2020,1,21,1850870114,1808240035,true +2020,1,22,126235055,1157419545,false +2020,1,22,434936831,1833090475,false +2020,1,22,1023157618,1297150826,true +2020,1,22,838309611,1196143353,false +2020,1,22,1142374972,1215705390,false +2020,1,22,531398545,1796385449,false +2020,1,22,1537181280,1499511158,false +2020,1,22,392750303,2094670495,false +2020,1,22,2130890028,1824813168,true +2020,1,22,293129295,1747477996,false +2020,1,23,2098678436,669018763,false +2020,1,23,550825391,1948852494,false +2020,1,23,725617790,722615460,true +2020,1,23,18124687,1913874195,true +2020,1,23,1445740070,1030568359,true +2020,1,23,186779486,1818233031,true +2020,1,23,1704771511,1276820023,true +2020,1,23,665356180,335664489,false +2020,1,23,1769070627,2084267560,false +2020,1,23,2036224899,1138974886,true +2020,1,24,751913229,1736407293,false +2020,1,24,302609318,2121911245,false +2020,1,24,2003465976,1967911334,false +2020,1,24,1063382656,153280620,false +2020,1,24,1991115479,174381867,false +2020,1,24,1633954160,614406369,true +2020,1,24,975477294,2140614417,true +2020,1,24,1717221890,878317447,true +2020,1,24,1657821493,1445890962,true +2020,1,24,1235774367,743491447,true +2020,1,25,556555974,697886475,false +2020,1,25,187883192,320104996,true +2020,1,25,1372091388,2143477534,true +2020,1,25,612023736,1514666809,true +2020,1,25,257069510,1724779911,false +2020,1,25,985162072,1966225528,false +2020,1,25,956202213,1573207439,false +2020,1,25,96514361,1022094743,true +2020,1,25,2080419076,1114850466,false +2020,1,25,1969055439,1919124554,true +2020,1,26,1949879798,536669559,true +2020,1,26,658663412,268769014,true +2020,1,26,936407603,544123073,false +2020,1,26,731251655,1959975109,false +2020,1,26,765629488,786358131,false +2020,1,26,1621051304,85708823,false +2020,1,26,1177180203,1711710701,false +2020,1,26,1199682399,340375677,true +2020,1,26,1625076346,2133742766,true +2020,1,26,1496521441,1885140933,false +2020,1,27,1174812285,991606379,true +2020,1,27,1341378855,448067008,true +2020,1,27,1832448205,639728787,true +2020,1,27,264510226,77162691,false +2020,1,27,818797368,1665309344,false +2020,1,27,1330623066,1507285278,true +2020,1,27,453100450,363619794,true +2020,1,27,287940592,1251691509,true +2020,1,27,1645311733,566426148,true +2020,1,27,2102016607,1215119931,false +2020,1,28,803157913,1640357526,false +2020,1,28,1489924113,1692308174,true +2020,1,28,1803160361,1559148068,false +2020,1,28,549193346,1430531797,true +2020,1,28,54775147,635904005,false +2020,1,28,1832971974,1358439929,false +2020,1,28,1617839788,713987326,true +2020,1,28,1032097007,387273745,false +2020,1,28,361910802,1357930744,true +2020,1,28,623813430,400659531,false +2020,1,29,577928325,2022219543,false +2020,1,29,1297074928,982169099,false +2020,1,29,664796983,474065916,false +2020,1,29,1735382204,1848987113,true +2020,1,29,346857721,1081844992,false +2020,1,29,2015190633,2063231389,false +2020,1,29,747925468,1954059401,false +2020,1,29,812333186,1634318262,false +2020,1,29,811694291,999992009,true +2020,1,29,1576085136,1750330479,true +2020,1,30,842216779,1847633793,true +2020,1,30,827911263,1602724608,false +2020,1,30,536372981,856110245,false +2020,1,30,2103101425,62373695,true +2020,1,30,1402017854,838709997,false +2020,1,30,939997933,1072887147,true +2020,1,30,408388640,561557695,false +2020,1,30,1841475489,1182379635,true +2020,1,30,1415139920,1655455987,false +2020,1,30,1895490799,1440910622,false +2020,1,31,460975360,1919580764,true +2020,1,31,1877504450,1394767069,false +2020,1,31,1196772260,2070274587,true +2020,1,31,2002250722,1817619756,false +2020,1,31,155087483,1251029554,true +2020,1,31,234331960,1738908877,true +2020,1,31,1445177556,1292116676,false +2020,1,31,1999575522,268140101,false +2020,1,31,1993676860,842754055,false +2020,1,31,239955626,564986717,false +2020,2,1,1647424797,640042977,false +2020,2,1,349684415,28274590,true +2020,2,1,807800187,1975570830,true +2020,2,1,465228988,1674756601,false +2020,2,1,1415345352,350433016,true +2020,2,1,2390624,614171678,false +2020,2,1,860687374,394168958,true +2020,2,1,452716427,992494125,true +2020,2,1,1633479644,519171420,false +2020,2,1,2042936407,1361958858,true +2020,2,2,2064665013,995498485,true +2020,2,2,1787210089,1102532532,true +2020,2,2,831350018,2012285610,true +2020,2,2,2101657798,320137587,true +2020,2,2,1280468898,980957452,true +2020,2,2,385390298,1258814654,false +2020,2,2,1793836216,799896194,false +2020,2,2,1477966450,1876194462,false +2020,2,2,572722248,1085511355,false +2020,2,2,378246095,2103282817,true +2020,2,3,1354708154,859797896,true +2020,2,3,1328675365,636676020,true +2020,2,3,511841126,1607144687,false +2020,2,3,811078689,328275970,true +2020,2,3,221623214,1321570715,true +2020,2,3,1713681225,777044587,true +2020,2,3,1499824212,1388198634,true +2020,2,3,2080611153,1248909605,true +2020,2,3,1525175567,1102154067,false +2020,2,3,902454722,1111058548,false +2020,2,4,1985650317,1605490907,false +2020,2,4,722464639,1201096692,true +2020,2,4,112934236,1749447361,true +2020,2,4,309096716,1701262438,false +2020,2,4,1903099004,75897022,true +2020,2,4,831560617,419788333,true +2020,2,4,306226818,290098531,false +2020,2,4,325088406,1674237675,true +2020,2,4,539896972,1029863542,false +2020,2,4,668572965,850416911,true +2020,2,5,1407481990,1112657765,false +2020,2,5,628531551,1403538986,false +2020,2,5,1772976234,383215912,false +2020,2,5,2097371233,601936229,false +2020,2,5,284181111,2124605983,false +2020,2,5,1872072910,1170866548,false +2020,2,5,1528230557,1944621018,true +2020,2,5,35991770,671281619,false +2020,2,5,1177395091,131196364,false +2020,2,5,1924756694,862244146,false +2020,2,6,187273802,211936077,true +2020,2,6,187261018,1736991584,false +2020,2,6,1120883897,184594567,false +2020,2,6,1837405515,202246927,true +2020,2,6,1895056704,2030379800,false +2020,2,6,190441846,1410801733,true +2020,2,6,1184346639,629619852,true +2020,2,6,1558318719,840360546,false +2020,2,6,250487883,1734632454,false +2020,2,6,1015005511,1369251061,true +2020,2,7,1949107444,517081516,false +2020,2,7,673910048,821980382,false +2020,2,7,1840657847,1776397745,false +2020,2,7,786599015,172894061,false +2020,2,7,1534273065,2127509009,true +2020,2,7,218760022,213285757,false +2020,2,7,393832995,369812831,false +2020,2,7,434670384,1078551742,false +2020,2,7,1909682235,1065841204,false +2020,2,7,524194585,23922602,true +2020,2,8,8241032,1084523037,false +2020,2,8,130257201,2062031064,false +2020,2,8,400206388,281113628,true +2020,2,8,1336420816,1185848494,false +2020,2,8,1625142922,669828634,false +2020,2,8,2109809649,164078745,true +2020,2,8,1891545730,1829488089,true +2020,2,8,232556848,239355229,false +2020,2,8,2035779070,1429502278,true +2020,2,8,632888552,1790764566,false +2020,2,9,1098118696,1669466945,false +2020,2,9,357218643,996788737,false +2020,2,9,2138552507,1423532332,true +2020,2,9,787966651,1011312425,true +2020,2,9,1178402851,1616386793,true +2020,2,9,734142487,840911495,true +2020,2,9,379003302,1186591244,false +2020,2,9,756731319,168756796,true +2020,2,9,927771935,719750758,false +2020,2,9,409516474,749437160,false +2020,2,10,1365266937,1650521405,false +2020,2,10,352037776,381093204,false +2020,2,10,1668140892,568421830,false +2020,2,10,2029694644,388118491,true +2020,2,10,1830354387,1051402433,true +2020,2,10,570644058,1679666017,true +2020,2,10,2062110427,818657865,true +2020,2,10,618185428,1781318074,false +2020,2,10,1795160838,1573379114,false +2020,2,10,1286431433,1664607173,false +2020,2,11,156635597,1675500645,false +2020,2,11,1261590942,262699953,false +2020,2,11,653313565,1232187053,false +2020,2,11,209316261,358385698,true +2020,2,11,719075645,347760889,false +2020,2,11,1891306468,1093773613,false +2020,2,11,1131002284,156373636,false +2020,2,11,1067503761,1547911273,false +2020,2,11,1157594620,966865360,false +2020,2,11,1662716579,312423300,false +2020,2,12,474637276,1140304265,false +2020,2,12,949712382,41362720,true +2020,2,12,1148893645,364426586,true +2020,2,12,1642384160,219027938,true +2020,2,12,962831926,987041552,false +2020,2,12,1885035625,499486708,true +2020,2,12,1968761042,713654694,true +2020,2,12,1469167684,1117887860,true +2020,2,12,1787005871,413778693,false +2020,2,12,587313373,780519039,true +2020,2,13,545664823,2087085731,true +2020,2,13,2115691828,63927767,false +2020,2,13,910360821,1086188103,true +2020,2,13,2003093669,1404463963,true +2020,2,13,1724755741,1010996120,true +2020,2,13,594672949,355310019,true +2020,2,13,633592569,968679492,false +2020,2,13,652603984,186245479,true +2020,2,13,1423915721,1421999073,false +2020,2,13,531229887,880895988,false +2020,2,14,939644862,838456790,false +2020,2,14,776680939,1609764185,false +2020,2,14,1925852942,1700093676,true +2020,2,14,440795103,1971731546,false +2020,2,14,295273824,1908734301,false +2020,2,14,1086185595,822616453,true +2020,2,14,1939407352,2016710516,false +2020,2,14,1529442770,516144513,false +2020,2,14,1748253544,1416553501,false +2020,2,14,1056437945,291594220,true +2020,2,15,195252667,1715918332,true +2020,2,15,2000726495,488271427,true +2020,2,15,1969263183,1189824730,false +2020,2,15,760212242,1561722505,false +2020,2,15,100827099,1908574862,true +2020,2,15,1316528502,1437028268,false +2020,2,15,432211281,83042226,false +2020,2,15,1056125662,1050778072,true +2020,2,15,798434460,1287799222,true +2020,2,15,556806023,1367711534,false +2020,2,16,561687156,1213319994,false +2020,2,16,1518180063,1341186471,true +2020,2,16,544990893,125774010,true +2020,2,16,1413625125,1107819782,true +2020,2,16,328346840,1123021533,true +2020,2,16,661936893,651455521,false +2020,2,16,1742504720,1636658459,true +2020,2,16,492234587,1182828270,false +2020,2,16,312309057,812781169,true +2020,2,16,71407937,1293212440,true +2020,2,17,36573517,765512529,true +2020,2,17,1385542221,1389998853,true +2020,2,17,1920511202,824172315,false +2020,2,17,1549626384,1664860051,true +2020,2,17,989319128,2000986306,false +2020,2,17,39350675,139982587,true +2020,2,17,1354597996,1384468837,false +2020,2,17,1973404585,1753802648,true +2020,2,17,1643775167,1490296068,false +2020,2,17,241433043,1008633729,false +2020,2,18,691009879,68453533,true +2020,2,18,226427617,1958478396,false +2020,2,18,1405795992,2099015253,false +2020,2,18,195637149,297778161,true +2020,2,18,794549104,1279521649,false +2020,2,18,981778205,529974760,true +2020,2,18,1345749418,246154583,true +2020,2,18,418195340,1333518905,false +2020,2,18,958465188,1987246537,false +2020,2,18,247676816,277087469,true +2020,2,19,286600539,1579633680,false +2020,2,19,892210074,102207245,false +2020,2,19,1966860556,1888105444,true +2020,2,19,483628983,1558469708,false +2020,2,19,1930536410,877423650,false +2020,2,19,258851267,2074392141,true +2020,2,19,724882745,1913942710,false +2020,2,19,1293777170,1063348415,true +2020,2,19,995954190,316473415,true +2020,2,19,100306910,2004359093,false +2020,2,20,700987585,1387282817,false +2020,2,20,203364388,76852748,true +2020,2,20,1504484798,1957547280,false +2020,2,20,10041528,437177284,false +2020,2,20,336505611,746141874,true +2020,2,20,1197260974,730307722,false +2020,2,20,1098080258,1608637663,false +2020,2,20,1750814323,639499502,false +2020,2,20,1454085719,1168349694,false +2020,2,20,214758696,2035704052,false +2020,2,21,809985513,689412898,false +2020,2,21,1588573725,1688803589,false +2020,2,21,190166289,2084303293,false +2020,2,21,1799304576,416246382,true +2020,2,21,369899884,172739282,false +2020,2,21,479893652,1158219493,true +2020,2,21,1952763223,84916615,true +2020,2,21,10537239,931517142,false +2020,2,21,1175624851,2007987243,true +2020,2,21,1883061402,931736672,false +2020,2,22,1911838408,745748493,false +2020,2,22,1751394296,2075465037,false +2020,2,22,455074022,1323509166,false +2020,2,22,2066581044,98914409,false +2020,2,22,2027615888,438573626,false +2020,2,22,132964187,667467525,true +2020,2,22,326984471,1564157012,true +2020,2,22,886052680,616932740,false +2020,2,22,796065201,170885514,false +2020,2,22,1945832794,1939539532,true +2020,2,23,1913321197,656238356,true +2020,2,23,1513573751,2090109038,false +2020,2,23,389567892,1623557034,true +2020,2,23,1608962035,650736602,true +2020,2,23,1175801716,276003224,true +2020,2,23,775199031,1525923593,true +2020,2,23,852788944,1468225362,true +2020,2,23,424493886,353831511,false +2020,2,23,161722211,1540334139,false +2020,2,23,26274357,1357726602,false +2020,2,24,814859834,1563994719,false +2020,2,24,2145250031,1945606193,true +2020,2,24,1162767822,512091974,false +2020,2,24,1912876266,1895688166,true +2020,2,24,2140812333,918805812,false +2020,2,24,209756943,1901076380,true +2020,2,24,1572477709,316067692,false +2020,2,24,667096445,864443769,true +2020,2,24,1025554971,1435770707,true +2020,2,24,1280406519,1165263082,false +2020,2,25,701059564,1235417476,false +2020,2,25,1385542616,1414098169,true +2020,2,25,1674916437,2146412855,false +2020,2,25,103867790,531813133,false +2020,2,25,230261013,1781786502,true +2020,2,25,2040565005,547646107,false +2020,2,25,370525719,1569165172,true +2020,2,25,69512644,596711018,false +2020,2,25,1055733946,330799304,true +2020,2,25,1369257451,1482764932,false +2020,2,26,543927768,1231678226,false +2020,2,26,2008929870,1065496033,true +2020,2,26,979421319,1348063097,false +2020,2,26,762227421,715078002,true +2020,2,26,4833232,910578470,true +2020,2,26,292468132,1786989082,true +2020,2,26,1558985159,2024742239,false +2020,2,26,1778966787,1100709291,false +2020,2,26,1358395594,1014490035,true +2020,2,26,1768707896,1863004946,true +2020,2,27,2093478357,514528205,true +2020,2,27,432688275,1263887399,true +2020,2,27,676555564,262360512,false +2020,2,27,614739704,1698241510,false +2020,2,27,931629254,752628456,true +2020,2,27,300505202,376491514,true +2020,2,27,1021591620,355380410,false +2020,2,27,213398534,377547998,true +2020,2,27,1126735957,140866587,true +2020,2,27,934742798,444854788,false +2020,2,28,1757919189,1521229069,true +2020,2,28,1981333175,1617899453,false +2020,2,28,606717711,1030130648,false +2020,2,28,544429183,226084572,true +2020,2,28,1874022882,1824477181,true +2020,2,28,2121582026,482806344,true +2020,2,28,1914680256,897138321,true +2020,2,28,1585889758,1024985337,false +2020,2,28,866815674,2035390666,true +2020,2,28,924210922,1500574485,false +2020,2,29,1479370013,836461814,true +2020,2,29,124293605,273295651,true +2020,2,29,42660165,609169157,true +2020,2,29,1570399846,749800598,true +2020,2,29,1398665650,1577745264,false +2020,2,29,1736926920,1875947290,false +2020,2,29,1347825549,703308333,false +2020,2,29,554193456,136860261,true +2020,2,29,912593935,1195301086,false +2020,2,29,175509843,94297220,false +2020,2,30,1702313360,1976220301,true +2020,2,30,1021228051,427558098,false +2020,2,30,486391599,1889134735,false +2020,2,30,1051013445,902208541,false +2020,2,30,1331633733,413549402,true +2020,2,30,779640633,2019753643,true +2020,2,30,1899775785,238814080,false +2020,2,30,1381345004,1737820442,false +2020,2,30,855811821,1465940488,true +2020,2,30,242590941,831054155,false +2020,2,31,921954843,974374103,false +2020,2,31,879694337,1907366622,false +2020,2,31,507158684,69607149,false +2020,2,31,1591426125,846414566,true +2020,2,31,218482003,525464992,true +2020,2,31,1690360262,350240993,false +2020,2,31,755758088,2106745626,false +2020,2,31,2056874531,1721584404,false +2020,2,31,1448182463,742550470,false +2020,2,31,757824094,116849373,true +2020,3,1,78498705,1576152949,true +2020,3,1,1936409285,395471260,true +2020,3,1,743328406,958101589,false +2020,3,1,763564909,1312143674,true +2020,3,1,1845483408,1594589467,false +2020,3,1,448209946,590418818,true +2020,3,1,273466696,1013104879,false +2020,3,1,132871153,234262972,true +2020,3,1,239804645,1114691317,false +2020,3,1,1485750926,313176659,false +2020,3,2,105462180,1980735628,true +2020,3,2,1614356319,1801622636,true +2020,3,2,500962747,749202585,true +2020,3,2,1415472457,1821258160,true +2020,3,2,692523849,1759013751,true +2020,3,2,1338640385,754437316,true +2020,3,2,1921301663,260456036,true +2020,3,2,1820863915,309253913,false +2020,3,2,1974295887,1482240645,true +2020,3,2,227277456,96680734,true +2020,3,3,379730369,928846549,false +2020,3,3,139560460,298405884,true +2020,3,3,507903286,1111482775,true +2020,3,3,2026239757,1170297439,false +2020,3,3,1981354633,1143080236,true +2020,3,3,158208946,1843732990,false +2020,3,3,882358799,866309459,false +2020,3,3,1375007461,538035680,true +2020,3,3,1478482782,1022381379,false +2020,3,3,2069153811,534309257,true +2020,3,4,966089705,1499218975,true +2020,3,4,283361350,1574024093,true +2020,3,4,14695347,776618156,true +2020,3,4,631827788,1489769111,true +2020,3,4,407531391,2113279582,true +2020,3,4,123279035,901871265,false +2020,3,4,1999795282,195676429,true +2020,3,4,1201293794,592508861,false +2020,3,4,1837660704,815729135,false +2020,3,4,1837496591,1973005746,true +2020,3,5,994038765,1081199544,true +2020,3,5,57980856,1769902971,false +2020,3,5,1977461214,1242250467,true +2020,3,5,1577710688,375210553,false +2020,3,5,1491912163,1550958222,false +2020,3,5,1189661264,1753821123,false +2020,3,5,848317947,1162312574,false +2020,3,5,1948906625,1766620020,false +2020,3,5,1993041743,841475606,false +2020,3,5,1160011846,1715322824,false +2020,3,6,1194894225,514321776,false +2020,3,6,888777110,60778975,false +2020,3,6,105107032,705034457,false +2020,3,6,1054555146,1798557705,true +2020,3,6,1075626624,1901592149,true +2020,3,6,754367211,1948006111,true +2020,3,6,1842835380,1399792641,false +2020,3,6,1361379294,470169757,false +2020,3,6,770225821,132816235,false +2020,3,6,1949319957,859317425,true +2020,3,7,959008381,724938034,true +2020,3,7,701966608,2125135296,false +2020,3,7,704581913,1289695378,false +2020,3,7,1422676300,83380883,true +2020,3,7,100983352,894021032,false +2020,3,7,1040115321,1700203167,false +2020,3,7,262962735,547411594,true +2020,3,7,265317384,393828657,true +2020,3,7,1882840867,1834066578,false +2020,3,7,1083696693,1752871521,false +2020,3,8,910371602,1381160040,true +2020,3,8,599871706,1109652306,true +2020,3,8,973819071,2101923501,false +2020,3,8,64124580,1133519734,false +2020,3,8,1053362198,1795574361,true +2020,3,8,892956045,744318660,true +2020,3,8,1516541599,1530833189,true +2020,3,8,1768724129,787298596,true +2020,3,8,565588765,1593478043,false +2020,3,8,1950142077,1597690245,false +2020,3,9,524780400,1697502633,false +2020,3,9,1846464770,30299983,true +2020,3,9,1853822533,562738617,false +2020,3,9,1997831735,2023685939,true +2020,3,9,1211252297,1155031501,true +2020,3,9,1804009297,1033911433,true +2020,3,9,59770576,900227847,true +2020,3,9,1713017426,1199147702,false +2020,3,9,601153076,183124900,true +2020,3,9,1029722057,1147860570,true +2020,3,10,439775316,830248030,false +2020,3,10,408641933,2094534700,false +2020,3,10,76683260,965891486,false +2020,3,10,905521811,979919825,true +2020,3,10,318101540,1866280472,true +2020,3,10,8888106,341867285,true +2020,3,10,574031417,1351437215,true +2020,3,10,387240514,746910283,true +2020,3,10,70599934,1387701327,false +2020,3,10,1504073610,1826471104,true +2020,3,11,55718810,1082482018,true +2020,3,11,859822988,1963556995,true +2020,3,11,994825974,1102492241,false +2020,3,11,881134090,1460041462,false +2020,3,11,1071272819,518824461,false +2020,3,11,1148442699,1396282332,false +2020,3,11,117021602,682506599,true +2020,3,11,1733254174,1238329061,true +2020,3,11,933844104,660035861,false +2020,3,11,58184116,1149848554,false +2020,3,12,780921479,1002808008,true +2020,3,12,2034180523,1622252623,false +2020,3,12,1005632024,1763089491,false +2020,3,12,1640525271,1416806471,true +2020,3,12,579524036,176185560,true +2020,3,12,976795306,1166985460,false +2020,3,12,561538860,1219180392,true +2020,3,12,278649943,1696773268,false +2020,3,12,2132245246,2714534,true +2020,3,12,178710010,1972122684,false +2020,3,13,1550927905,1373347918,false +2020,3,13,1914365546,1836109518,false +2020,3,13,1968783500,2027672741,false +2020,3,13,1749342597,1117200169,false +2020,3,13,875136628,1241992511,true +2020,3,13,1097448980,35634131,false +2020,3,13,1018841827,827313577,false +2020,3,13,435933079,713794685,true +2020,3,13,121647822,1341973452,true +2020,3,13,1507354784,1467346484,false +2020,3,14,780252740,1776664357,false +2020,3,14,80580988,1164534056,true +2020,3,14,642945360,1751121205,true +2020,3,14,416014431,119530302,true +2020,3,14,450125988,452348324,false +2020,3,14,2134107992,501182709,false +2020,3,14,1304785953,550403087,true +2020,3,14,132532645,54073923,false +2020,3,14,2106281947,1819758213,false +2020,3,14,1286112587,1191414644,true +2020,3,15,2105626677,2041884438,false +2020,3,15,1357240065,479275566,false +2020,3,15,1205927953,1019960315,true +2020,3,15,829435333,1065241898,false +2020,3,15,1098381407,1146797391,true +2020,3,15,299841724,772186439,false +2020,3,15,301278823,894572613,false +2020,3,15,1820788007,169408760,true +2020,3,15,1171729050,1366103906,true +2020,3,15,2049029013,1398905667,true +2020,3,16,1490072546,642615338,false +2020,3,16,2079019676,2108112330,true +2020,3,16,1344494484,559213110,false +2020,3,16,1031289531,1974745488,false +2020,3,16,1663706268,1915309240,true +2020,3,16,1002383926,1593953409,true +2020,3,16,1499098237,879021774,false +2020,3,16,1474046218,2083482079,false +2020,3,16,2095867694,426128445,true +2020,3,16,202423372,1600965154,true +2020,3,17,653145870,1248408420,true +2020,3,17,1582003595,349087474,false +2020,3,17,934340696,1067301510,true +2020,3,17,1221346835,613902911,true +2020,3,17,1730354141,13842884,false +2020,3,17,495812390,1987944360,false +2020,3,17,423071476,645813335,true +2020,3,17,346677115,1705000358,true +2020,3,17,862387936,834143979,false +2020,3,17,344378282,1286008902,true +2020,3,18,950907930,1485480491,false +2020,3,18,269787725,1316225354,false +2020,3,18,248077887,494780384,false +2020,3,18,1351439871,1823693320,true +2020,3,18,1820812180,1779099182,true +2020,3,18,1498408579,1890777455,true +2020,3,18,1243443038,1653256146,false +2020,3,18,1446791082,925181160,true +2020,3,18,1292660868,104946039,false +2020,3,18,683839463,1263215897,true +2020,3,19,1622896171,719475571,true +2020,3,19,1808742786,1911265198,true +2020,3,19,2009410132,1087323275,true +2020,3,19,76523758,1751373238,true +2020,3,19,1116637861,479151508,true +2020,3,19,1112692658,1183232569,false +2020,3,19,2112404958,43106063,false +2020,3,19,515819223,1347516170,true +2020,3,19,1751195822,1346914562,true +2020,3,19,1949697263,1323013644,false +2020,3,20,403803869,760421068,true +2020,3,20,846066432,2018415452,false +2020,3,20,2020156946,1730800545,true +2020,3,20,156358906,493766940,true +2020,3,20,1141511572,1572699624,true +2020,3,20,337253769,1357471711,true +2020,3,20,288553688,1441103558,false +2020,3,20,1968969305,2069216985,true +2020,3,20,180292798,1179295843,false +2020,3,20,406461740,1655922019,true +2020,3,21,443615176,1722376409,true +2020,3,21,1032844113,636179323,true +2020,3,21,1418329013,330571985,false +2020,3,21,527720539,1141561661,false +2020,3,21,1009440573,210097196,true +2020,3,21,1609454120,1244603791,true +2020,3,21,1968793830,1525548119,false +2020,3,21,702346139,822893172,true +2020,3,21,2139526189,1751965462,true +2020,3,21,426886297,1650391490,true +2020,3,22,1358577781,1295381992,true +2020,3,22,808382753,1260144429,true +2020,3,22,926161782,1247998292,false +2020,3,22,2079848088,764253420,false +2020,3,22,103824172,125771968,true +2020,3,22,202501547,1749503577,true +2020,3,22,1654197099,1044990625,true +2020,3,22,1070062664,1600436320,false +2020,3,22,631329194,1388036641,false +2020,3,22,209196704,1479696798,true +2020,3,23,2088861088,68837012,false +2020,3,23,314392377,837037171,true +2020,3,23,1411024841,964349517,false +2020,3,23,171488619,1555647363,false +2020,3,23,1720276722,482062777,false +2020,3,23,662669031,1914996043,false +2020,3,23,1587715152,2017543365,false +2020,3,23,1082004501,334310879,false +2020,3,23,737040857,1072688116,true +2020,3,23,486725743,1579453930,true +2020,3,24,1315530970,540282816,false +2020,3,24,632094498,1344435331,false +2020,3,24,457402098,1119296530,true +2020,3,24,1227352468,179602811,true +2020,3,24,1395195398,1112574702,false +2020,3,24,290939053,2103969831,true +2020,3,24,349280200,1355442353,false +2020,3,24,601891570,267033308,true +2020,3,24,1346357216,1003949423,true +2020,3,24,735228103,710397330,true +2020,3,25,1838424035,1025223250,true +2020,3,25,1107881345,1450201708,true +2020,3,25,870925968,904663822,true +2020,3,25,963524365,284343566,false +2020,3,25,1659379732,1838721724,false +2020,3,25,460998477,715702806,false +2020,3,25,1153771330,705814722,false +2020,3,25,256137608,1533615628,true +2020,3,25,1937889279,455411092,true +2020,3,25,1976171380,980144030,true +2020,3,26,1351797078,775274599,false +2020,3,26,1602057418,747221779,true +2020,3,26,356311530,1494017594,true +2020,3,26,1346771297,152755500,false +2020,3,26,1005157320,1960914874,true +2020,3,26,131201350,1280893422,true +2020,3,26,1731028165,1683889895,true +2020,3,26,1155958600,1382518806,false +2020,3,26,1174666978,871407251,false +2020,3,26,1550633455,1799562263,false +2020,3,27,1379166329,1692161284,true +2020,3,27,145673185,277192179,true +2020,3,27,1504712224,87988326,true +2020,3,27,1010702357,834423646,false +2020,3,27,1518450806,333987533,false +2020,3,27,862874473,485280733,true +2020,3,27,1029256804,742094076,false +2020,3,27,2140813436,1260873438,true +2020,3,27,1881276536,720449063,true +2020,3,27,1044345814,227052550,false +2020,3,28,741842011,1890511981,false +2020,3,28,1877421502,433673250,false +2020,3,28,1043861401,1210279103,false +2020,3,28,1490031706,1366746494,true +2020,3,28,1915442991,1159866978,true +2020,3,28,341763043,589973651,false +2020,3,28,638348700,1581051235,false +2020,3,28,1252875291,579375683,false +2020,3,28,787813230,636370058,false +2020,3,28,1573607743,773381925,true +2020,3,29,1056353913,956692133,false +2020,3,29,648707120,164659083,false +2020,3,29,1553248827,1340340314,false +2020,3,29,1994207372,960198211,true +2020,3,29,1283993637,337177262,true +2020,3,29,375466207,133766222,false +2020,3,29,332921012,37205001,false +2020,3,29,294938696,1440934391,false +2020,3,29,999304867,1655816288,true +2020,3,29,31782422,1812603414,false +2020,3,30,433185699,973893322,false +2020,3,30,157692752,715388510,true +2020,3,30,1657625168,1583563184,false +2020,3,30,1063045928,789169163,true +2020,3,30,1431137128,367477836,true +2020,3,30,781856445,926600780,false +2020,3,30,799628395,622084931,false +2020,3,30,1317718473,1588621370,false +2020,3,30,76858406,264062833,false +2020,3,30,1459908863,1395044924,false +2020,3,31,812578276,2110130503,true +2020,3,31,1352418380,2139000614,true +2020,3,31,374892077,1416831051,false +2020,3,31,738799161,2008126330,true +2020,3,31,1582015590,1688229267,false +2020,3,31,1814601499,86502801,true +2020,3,31,1549757097,1657211228,true +2020,3,31,586804700,1905921940,true +2020,3,31,1482291623,1524030210,true +2020,3,31,2068789624,1773714572,false +2020,4,1,1882523785,419104781,true +2020,4,1,479956359,511987710,false +2020,4,1,1344934128,2070876685,true +2020,4,1,900315506,1314149310,false +2020,4,1,1635597745,460225648,false +2020,4,1,272600350,1928370692,false +2020,4,1,100781800,1409067688,false +2020,4,1,1418286891,1580605937,false +2020,4,1,2134700131,1307272242,true +2020,4,1,529686569,251435766,false +2020,4,2,1503582117,89528158,false +2020,4,2,1693647689,1139898339,false +2020,4,2,1768953067,419228900,false +2020,4,2,1867759727,250002574,true +2020,4,2,165701365,1162472880,true +2020,4,2,1665631417,1812797771,true +2020,4,2,1258464139,594686316,false +2020,4,2,400375251,1080626597,true +2020,4,2,1420261098,1153505194,true +2020,4,2,1069148588,88326185,false +2020,4,3,635923110,561594053,true +2020,4,3,468092783,1003195159,false +2020,4,3,1051713700,2126058184,false +2020,4,3,1273489355,1344994899,true +2020,4,3,131729712,365149183,true +2020,4,3,1033775599,524539124,false +2020,4,3,191193955,157071368,true +2020,4,3,810878909,1812343101,true +2020,4,3,647470421,1898434931,false +2020,4,3,657506095,1897569257,false +2020,4,4,1828961741,611928400,false +2020,4,4,527611868,526072841,false +2020,4,4,709096297,292367174,false +2020,4,4,225954584,1633506029,false +2020,4,4,2011776380,618708995,true +2020,4,4,1652859444,1186716623,true +2020,4,4,75972880,1135555361,false +2020,4,4,1459929528,987558192,true +2020,4,4,388475084,876189918,false +2020,4,4,1179771324,1834805806,false +2020,4,5,1458747567,1228070191,true +2020,4,5,5501601,220254273,false +2020,4,5,2119091939,361208088,true +2020,4,5,695268056,1472942612,true +2020,4,5,598245650,1912579160,false +2020,4,5,1155945860,1107751550,true +2020,4,5,594135553,564152507,true +2020,4,5,659913487,1670183626,true +2020,4,5,1683873119,1312330172,false +2020,4,5,1435530254,38354369,true +2020,4,6,510586890,553721405,false +2020,4,6,1499218953,1695092035,true +2020,4,6,232034619,962292594,false +2020,4,6,599256322,675903049,false +2020,4,6,1060547921,727488354,true +2020,4,6,220399053,569046950,true +2020,4,6,1696255291,812738178,false +2020,4,6,281037976,2023491047,false +2020,4,6,1252988328,636402518,true +2020,4,6,1556536376,395827993,true +2020,4,7,410988429,1897789473,false +2020,4,7,1707816469,1745607530,true +2020,4,7,296531877,830521862,false +2020,4,7,1701675969,92164049,true +2020,4,7,208708839,1519755820,false +2020,4,7,460266155,662695500,false +2020,4,7,431117878,1832311999,false +2020,4,7,570603884,944024407,false +2020,4,7,1287140785,1579144935,false +2020,4,7,677575059,1401238174,false +2020,4,8,1754082408,1964137482,false +2020,4,8,1184127500,123876637,false +2020,4,8,819792751,35409911,true +2020,4,8,1117054313,528877873,true +2020,4,8,1760798580,1069395655,true +2020,4,8,192224445,2026108225,false +2020,4,8,1733376625,1943368861,true +2020,4,8,1827949256,1344960518,true +2020,4,8,1991295756,1073673865,false +2020,4,8,830932129,1476266401,true +2020,4,9,981474237,1626778421,false +2020,4,9,111686694,1295693561,true +2020,4,9,1049523172,2018585905,true +2020,4,9,1375987162,1987489055,true +2020,4,9,1027425085,747024879,false +2020,4,9,1135937266,99081385,true +2020,4,9,2137377923,65214414,false +2020,4,9,1191942600,573594934,false +2020,4,9,1839554085,67853004,false +2020,4,9,1820095980,584365994,false +2020,4,10,1220266060,886473395,false +2020,4,10,1181531780,2006763966,false +2020,4,10,1585481128,859653852,false +2020,4,10,507880645,1809522671,true +2020,4,10,1717430472,89227994,true +2020,4,10,549854621,41057020,true +2020,4,10,1004523996,1185905846,false +2020,4,10,744143095,77089906,true +2020,4,10,1103761718,755415161,false +2020,4,10,327423507,270227950,true +2020,4,11,2017868010,1665660472,false +2020,4,11,209998822,1719990566,false +2020,4,11,269569109,680054499,true +2020,4,11,798926225,909805269,true +2020,4,11,409929338,412844417,true +2020,4,11,1141027384,1241803038,false +2020,4,11,1066756209,273200546,false +2020,4,11,278523118,1597778435,true +2020,4,11,888305502,577802540,false +2020,4,11,685206642,229085438,true +2020,4,12,1253938604,1808092882,true +2020,4,12,1784957926,1478634215,false +2020,4,12,920864126,1763301327,true +2020,4,12,260183252,1948251555,false +2020,4,12,231109263,527863488,false +2020,4,12,1484943381,1947007318,false +2020,4,12,793128964,1413663411,true +2020,4,12,1954360950,709364495,false +2020,4,12,476459730,178079938,true +2020,4,12,952824629,630707484,true +2020,4,13,490130376,124610517,false +2020,4,13,605043108,528293783,false +2020,4,13,576572519,43787609,true +2020,4,13,922772633,440680388,true +2020,4,13,2079008766,369220381,true +2020,4,13,1471074679,260644688,false +2020,4,13,1316771533,756383110,true +2020,4,13,1612831210,1260422308,false +2020,4,13,215094046,1974329053,false +2020,4,13,928782004,919124919,false +2020,4,14,72485011,503125866,false +2020,4,14,328164128,1574011691,false +2020,4,14,1392380949,472576909,true +2020,4,14,1688527850,458539935,false +2020,4,14,1679064875,1683750946,true +2020,4,14,1214315972,208676719,true +2020,4,14,2016488362,278383302,true +2020,4,14,1694140103,16496481,false +2020,4,14,474365169,1693449953,false +2020,4,14,1305207652,2004827378,false +2020,4,15,23892531,1805812093,true +2020,4,15,1607137978,1846207807,true +2020,4,15,1406110076,240606542,true +2020,4,15,963333975,1306429365,false +2020,4,15,85992825,202278273,true +2020,4,15,585294031,1417976344,false +2020,4,15,1604263972,1549599009,false +2020,4,15,83302323,290213760,false +2020,4,15,4041448,1105871415,false +2020,4,15,1789964487,1118515795,true +2020,4,16,1658673364,142086905,false +2020,4,16,832812428,218024304,true +2020,4,16,2110032379,424538260,true +2020,4,16,757587831,2130646789,false +2020,4,16,377272082,703442560,true +2020,4,16,22321263,1700989890,true +2020,4,16,1578746599,1358833244,false +2020,4,16,42333795,1127215973,false +2020,4,16,1522469709,1635407810,false +2020,4,16,808640798,912226270,false +2020,4,17,216655239,420085913,false +2020,4,17,542824603,1330622067,true +2020,4,17,626954854,809251273,false +2020,4,17,1801445261,651413159,false +2020,4,17,229184648,2092303710,false +2020,4,17,1311813845,1058010339,true +2020,4,17,128892801,2030449278,true +2020,4,17,1050406940,2126062338,true +2020,4,17,877093078,16390334,false +2020,4,17,1632060620,641294160,false +2020,4,18,1131977741,1401933756,false +2020,4,18,706188311,1735982757,true +2020,4,18,698222918,147464456,false +2020,4,18,2068425177,1277001887,false +2020,4,18,2043865618,861659574,false +2020,4,18,780001644,1980861730,true +2020,4,18,364145285,428750404,false +2020,4,18,1341429455,2143512753,false +2020,4,18,1593734331,368359052,true +2020,4,18,651994540,1252725044,true +2020,4,19,1434561345,534280435,true +2020,4,19,613338908,339240781,true +2020,4,19,1738750756,377131816,true +2020,4,19,1637865541,1939433545,true +2020,4,19,1786933398,1842080771,false +2020,4,19,454845213,235215692,true +2020,4,19,552296805,1153244510,false +2020,4,19,1386428413,1219067984,true +2020,4,19,278406843,1292430864,true +2020,4,19,1872282050,1686706694,false +2020,4,20,1277498335,1299147588,false +2020,4,20,195440562,961192901,false +2020,4,20,1584507374,881929698,true +2020,4,20,1484969827,1009629949,true +2020,4,20,168609990,1958327247,false +2020,4,20,494100048,316655610,true +2020,4,20,376658845,1798190627,true +2020,4,20,987870689,2101007305,false +2020,4,20,1877314544,767220911,false +2020,4,20,772693523,1183664551,false +2020,4,21,1979117526,532343905,true +2020,4,21,1306493441,1188982852,true +2020,4,21,55918813,95904108,true +2020,4,21,511158015,1298908085,true +2020,4,21,1191804657,763469636,false +2020,4,21,1483448838,585814775,false +2020,4,21,2010580743,1912229826,false +2020,4,21,407873693,1097395272,true +2020,4,21,835233660,1525514257,false +2020,4,21,356805005,86788548,true +2020,4,22,1941843051,1494242617,true +2020,4,22,1880585976,1317774347,true +2020,4,22,107334759,1062708063,false +2020,4,22,1821621933,222470869,false +2020,4,22,1501059672,1816402441,true +2020,4,22,2000594647,638100416,false +2020,4,22,1139325809,419226904,true +2020,4,22,2133947471,65031620,false +2020,4,22,2117986423,1381917997,false +2020,4,22,1088107836,1759081479,true +2020,4,23,306792402,158026675,false +2020,4,23,467752395,7566604,true +2020,4,23,803963420,2044970438,true +2020,4,23,2014418461,211262318,false +2020,4,23,1688315178,1546831536,false +2020,4,23,1136795588,852204463,false +2020,4,23,1922599372,480034227,true +2020,4,23,1326990946,1903832763,true +2020,4,23,1917947562,1956537428,true +2020,4,23,1037951558,696814377,true +2020,4,24,1642194703,23374189,true +2020,4,24,883129631,1304178550,true +2020,4,24,674006658,720394918,false +2020,4,24,1000572958,440769942,true +2020,4,24,1007757944,155640104,false +2020,4,24,1923274771,933651642,false +2020,4,24,1702607243,55345067,false +2020,4,24,1313111929,935768095,false +2020,4,24,1417950194,991481640,false +2020,4,24,131342177,506960444,false +2020,4,25,536236853,527043806,false +2020,4,25,2025897920,1563052459,false +2020,4,25,826394445,843249505,true +2020,4,25,2097380494,2134012316,false +2020,4,25,102321586,1401866738,true +2020,4,25,1360439475,663667139,false +2020,4,25,196852488,1938435155,true +2020,4,25,170049723,1651519980,true +2020,4,25,347736676,914029260,true +2020,4,25,768833220,837710789,true +2020,4,26,413117793,1386573093,true +2020,4,26,367957540,1140605311,false +2020,4,26,1267547781,235842747,false +2020,4,26,1365526710,1165576781,true +2020,4,26,344157083,1585719551,false +2020,4,26,1340192985,2062300660,false +2020,4,26,1088815125,1859010298,true +2020,4,26,1665593975,2074095329,true +2020,4,26,2104653712,228320173,false +2020,4,26,1325109947,409249410,true +2020,4,27,392625929,1850032744,true +2020,4,27,232069435,446769628,true +2020,4,27,1780897655,1889761137,false +2020,4,27,329307834,587410566,true +2020,4,27,620902066,786173063,false +2020,4,27,2132497625,1105804956,true +2020,4,27,289643510,369979433,false +2020,4,27,1993996109,1429880394,true +2020,4,27,431072107,1917163955,false +2020,4,27,1229017706,894652400,true +2020,4,28,865192044,211434402,true +2020,4,28,1085051954,726388760,false +2020,4,28,1510852743,91789988,true +2020,4,28,615986208,1969854652,false +2020,4,28,1309444472,1555966780,false +2020,4,28,964836199,230418304,false +2020,4,28,244126330,1500498214,false +2020,4,28,283617893,463045871,false +2020,4,28,975234510,546734783,false +2020,4,28,1388321129,1636601062,false +2020,4,29,671703367,770891798,true +2020,4,29,1758822964,872412839,false +2020,4,29,1571378491,369365116,false +2020,4,29,1478522029,1318635343,false +2020,4,29,627525418,1789579937,true +2020,4,29,487498940,659964834,true +2020,4,29,1468525045,393808552,false +2020,4,29,961024092,1479839341,true +2020,4,29,556058502,1308696249,false +2020,4,29,1157481523,517371625,true +2020,4,30,1935013929,190524917,true +2020,4,30,951505491,454287080,true +2020,4,30,1215013393,67327705,true +2020,4,30,1008677368,367502008,true +2020,4,30,1287972799,1087002994,false +2020,4,30,1574193242,2007194863,false +2020,4,30,340096574,687423226,true +2020,4,30,489986540,1246318256,true +2020,4,30,1144790129,1214718885,false +2020,4,30,985000099,1605447936,false +2020,4,31,2029931243,919410901,false +2020,4,31,2072420114,931614272,true +2020,4,31,3940364,161228007,false +2020,4,31,378939782,1405422324,false +2020,4,31,1406944844,18021210,true +2020,4,31,164795750,1576897361,true +2020,4,31,609712901,1177547520,false +2020,4,31,587779196,772962126,true +2020,4,31,207200792,749031793,true +2020,4,31,2095961339,1692242225,false +2020,5,1,90564415,264571395,false +2020,5,1,789182553,684257989,true +2020,5,1,272639986,347200496,true +2020,5,1,1856987736,566092738,false +2020,5,1,2100742428,1118314092,false +2020,5,1,257097422,166291472,true +2020,5,1,1101251098,1123275485,false +2020,5,1,429123604,863858404,false +2020,5,1,213129922,1076139128,false +2020,5,1,1244195103,569967601,false +2020,5,2,1594092629,318046693,false +2020,5,2,940522928,1911790112,true +2020,5,2,1549743535,1976178884,false +2020,5,2,895363105,1529187205,true +2020,5,2,1897009434,868809757,true +2020,5,2,551155637,1279249558,true +2020,5,2,1855268480,1339921961,true +2020,5,2,851575671,636985547,true +2020,5,2,2063408894,134923000,true +2020,5,2,1560329487,1825664297,true +2020,5,3,928544510,1356530903,false +2020,5,3,1089840142,1036231026,false +2020,5,3,1911612002,1144421512,true +2020,5,3,575561128,1219734249,false +2020,5,3,1891277419,2008338096,false +2020,5,3,936891862,1407847399,true +2020,5,3,1391587163,520492282,true +2020,5,3,102295113,394697153,true +2020,5,3,1000826891,1738589415,true +2020,5,3,1739447490,1675925151,true +2020,5,4,1269114850,1233184588,true +2020,5,4,1321545133,387542881,false +2020,5,4,688476150,1744954074,false +2020,5,4,1948317955,626353570,true +2020,5,4,522463805,741591376,true +2020,5,4,1857787623,1091562607,true +2020,5,4,289224220,244931116,true +2020,5,4,1196783233,319949401,false +2020,5,4,1409168302,1741166311,false +2020,5,4,256640246,379534826,false +2020,5,5,2111381401,1927324711,true +2020,5,5,1493787546,1219770777,true +2020,5,5,1882145626,1076729974,false +2020,5,5,1180580775,108847838,false +2020,5,5,1579228731,478254161,false +2020,5,5,1494108959,1909875696,true +2020,5,5,57510268,906256557,false +2020,5,5,2080844202,1741972046,true +2020,5,5,929703506,1771087949,false +2020,5,5,114462091,1091214383,false +2020,5,6,2079289034,153681273,false +2020,5,6,2121455608,314498399,true +2020,5,6,442153656,414781684,false +2020,5,6,810300806,1594783801,false +2020,5,6,100319272,1953929286,true +2020,5,6,1615198821,1334430145,false +2020,5,6,1656068896,556871860,true +2020,5,6,1689281434,654235658,true +2020,5,6,1466419852,88072570,true +2020,5,6,283729281,39346275,false +2020,5,7,1533206691,1934723880,true +2020,5,7,1647815988,1754971915,true +2020,5,7,1711895346,1005118255,true +2020,5,7,1952230836,76909656,false +2020,5,7,1778398344,38232569,false +2020,5,7,218172127,272411541,false +2020,5,7,1323145217,2108757808,true +2020,5,7,1651856281,964847108,false +2020,5,7,782306644,785873653,true +2020,5,7,1328689596,727062723,false +2020,5,8,474432572,2103335990,false +2020,5,8,1762746779,1395623563,false +2020,5,8,1137974803,1841089389,true +2020,5,8,2063430725,573118077,true +2020,5,8,664700269,1884726884,false +2020,5,8,1929760462,2040381307,false +2020,5,8,1656447973,656540884,true +2020,5,8,494569027,1920476098,true +2020,5,8,1448602767,1393526528,false +2020,5,8,1269463824,719127225,true +2020,5,9,1833467340,558120598,false +2020,5,9,2021703692,757905206,false +2020,5,9,1227360123,1909450612,false +2020,5,9,371046497,419858089,false +2020,5,9,1069497012,1538799926,true +2020,5,9,19840258,1355358700,false +2020,5,9,1679122069,80843653,false +2020,5,9,753685888,1847685794,true +2020,5,9,280791492,475199147,false +2020,5,9,331641162,1578809461,true +2020,5,10,363184823,807235283,true +2020,5,10,1811572166,1855954833,true +2020,5,10,644364517,1469686780,true +2020,5,10,1932137161,709717545,false +2020,5,10,1903818590,994472337,true +2020,5,10,1351893575,896153836,false +2020,5,10,1047276944,1444679706,false +2020,5,10,1714385252,461000373,true +2020,5,10,469566740,313313414,true +2020,5,10,1270251559,1219250650,false +2020,5,11,1594622940,2071473149,true +2020,5,11,897358456,961446374,false +2020,5,11,25870366,779334723,true +2020,5,11,2115950650,686523041,false +2020,5,11,2021932999,1547456515,false +2020,5,11,781575063,79218100,true +2020,5,11,1205555046,34735014,true +2020,5,11,1037515743,617464400,true +2020,5,11,149978308,313606790,false +2020,5,11,2038085766,99060370,false +2020,5,12,81248171,631129488,true +2020,5,12,1458017055,526659757,true +2020,5,12,144459655,822361020,false +2020,5,12,847759476,1099570678,true +2020,5,12,1182658279,653069795,true +2020,5,12,233955329,1429927009,true +2020,5,12,454200845,915694291,true +2020,5,12,681733756,192646948,false +2020,5,12,1684560897,1602808255,false +2020,5,12,1492108081,1375654243,true +2020,5,13,549179887,2005827661,true +2020,5,13,59535709,1610496575,false +2020,5,13,19441332,1824055037,true +2020,5,13,1498914257,1300773794,true +2020,5,13,1067682024,382001561,true +2020,5,13,919893624,1673209234,true +2020,5,13,1881787328,1936954092,true +2020,5,13,1893387422,801483255,false +2020,5,13,777082788,722512372,true +2020,5,13,644400139,1745919259,true +2020,5,14,1350658383,1250063434,false +2020,5,14,1475721288,2006267210,true +2020,5,14,63115979,1220873343,false +2020,5,14,27209389,1126152281,false +2020,5,14,1312135082,1819903110,false +2020,5,14,820885259,761253891,true +2020,5,14,29405774,1175727008,true +2020,5,14,869158410,196654679,true +2020,5,14,1715937821,1086692406,false +2020,5,14,1809316754,796303795,false +2020,5,15,1564189425,1140365726,false +2020,5,15,362370271,1593990137,true +2020,5,15,1317415976,1244415699,true +2020,5,15,1847594253,1477268228,true +2020,5,15,24060442,1569044088,true +2020,5,15,1527851065,1925234823,false +2020,5,15,1561956072,552440252,false +2020,5,15,1257997457,1534219742,false +2020,5,15,1749520345,715941638,true +2020,5,15,1074434538,2041853048,true +2020,5,16,1693416132,847866474,false +2020,5,16,663489456,1964537307,false +2020,5,16,192078648,1254678867,true +2020,5,16,952281676,300494464,true +2020,5,16,1749559644,272066544,true +2020,5,16,1449121881,56835310,false +2020,5,16,2007280836,1945209402,true +2020,5,16,408933690,1295936221,true +2020,5,16,24226734,2057903030,true +2020,5,16,936124914,288487503,false +2020,5,17,1833718620,255729727,false +2020,5,17,877588044,1159637214,false +2020,5,17,1071521931,1291076982,true +2020,5,17,1753500596,1360751018,true +2020,5,17,561805114,1968811952,true +2020,5,17,707362898,1638904500,false +2020,5,17,222027578,1019446903,true +2020,5,17,1662238791,359559304,true +2020,5,17,1968088895,585698301,true +2020,5,17,284894228,80255870,false +2020,5,18,526475883,174682122,true +2020,5,18,348109470,26884094,false +2020,5,18,1541854340,786614868,false +2020,5,18,497115720,136142735,true +2020,5,18,2029322078,2070612290,true +2020,5,18,1611757588,1104852116,true +2020,5,18,362688542,1596258689,true +2020,5,18,266515668,108417794,true +2020,5,18,2147136852,1020012536,false +2020,5,18,2015399880,86802040,false +2020,5,19,2081251120,379589380,true +2020,5,19,704021317,935201409,false +2020,5,19,119578934,1394161919,true +2020,5,19,2095606582,1339904996,true +2020,5,19,1558851895,764797300,false +2020,5,19,1775318671,1768991314,true +2020,5,19,553443717,1813468067,true +2020,5,19,1539513367,1523371981,false +2020,5,19,53822610,1961777172,false +2020,5,19,1104986119,1724106659,true +2020,5,20,1404434050,1953281128,false +2020,5,20,2093943674,2027626702,false +2020,5,20,759711057,314808244,true +2020,5,20,152514463,938887191,false +2020,5,20,1946217118,431553281,true +2020,5,20,2144091683,425315412,true +2020,5,20,355840576,759274698,false +2020,5,20,1209939297,752610192,true +2020,5,20,138333989,313902990,false +2020,5,20,1042360868,802474506,true +2020,5,21,366433331,319640272,true +2020,5,21,1152887342,103312447,false +2020,5,21,470636838,1918210843,false +2020,5,21,1970690491,393773959,true +2020,5,21,1008784276,316279009,false +2020,5,21,791444407,159015147,false +2020,5,21,840215205,464510805,false +2020,5,21,603150187,116622391,false +2020,5,21,135272490,1340310415,false +2020,5,21,1871484860,1341017471,true +2020,5,22,1635685381,1780423055,true +2020,5,22,1977524062,960030087,true +2020,5,22,1025859372,602810375,true +2020,5,22,871346821,206998998,false +2020,5,22,1318181296,1854262517,false +2020,5,22,561353210,2084225877,true +2020,5,22,1307128710,309722216,true +2020,5,22,1463813278,1733625546,false +2020,5,22,1185303048,2138963983,false +2020,5,22,578855873,416433303,true +2020,5,23,988722282,957905249,true +2020,5,23,1766939607,1522518395,false +2020,5,23,1464643475,1635703162,false +2020,5,23,548619887,1121256522,false +2020,5,23,708105755,1212442890,false +2020,5,23,1345854331,1295127773,false +2020,5,23,1264053090,889841634,false +2020,5,23,328091470,101574302,true +2020,5,23,839485686,1645629188,false +2020,5,23,121702352,548364677,false +2020,5,24,1722991554,921872707,true +2020,5,24,1367252701,280077920,false +2020,5,24,1759376066,1027544531,true +2020,5,24,617871568,772694965,false +2020,5,24,1742222119,1628450144,false +2020,5,24,127004065,909485736,true +2020,5,24,68363833,1277558139,false +2020,5,24,1697697830,1757294502,true +2020,5,24,1939489622,727838596,true +2020,5,24,949297739,40490180,true +2020,5,25,1491797207,510003035,false +2020,5,25,798445163,1973414933,false +2020,5,25,1508229329,908477305,true +2020,5,25,1862862091,1956423267,true +2020,5,25,1186543330,697457478,false +2020,5,25,893887394,442744592,false +2020,5,25,547312264,131199430,false +2020,5,25,441164852,1235386374,true +2020,5,25,913073595,118776296,true +2020,5,25,287495921,2069356802,true +2020,5,26,147898814,1703272237,false +2020,5,26,1843715601,840437668,false +2020,5,26,1674585187,624529496,false +2020,5,26,2059887010,2049245694,false +2020,5,26,845381177,482925411,true +2020,5,26,391087111,341927837,false +2020,5,26,1857827409,174465273,false +2020,5,26,1410832730,852192585,true +2020,5,26,131574446,1899033362,false +2020,5,26,1142936300,2013128162,false +2020,5,27,1208353907,2015180182,true +2020,5,27,1399113112,2073597738,true +2020,5,27,1845837163,998826253,true +2020,5,27,88816807,1846227810,true +2020,5,27,1706410115,1365647038,false +2020,5,27,1782774897,184353014,false +2020,5,27,406782279,823884499,false +2020,5,27,1574223625,25159608,true +2020,5,27,1710757516,977912925,true +2020,5,27,9380491,23502679,false +2020,5,28,1734705863,745026631,true +2020,5,28,947667947,1157668114,true +2020,5,28,1992991932,99773340,false +2020,5,28,1068608087,1070345948,false +2020,5,28,2128229778,869865133,false +2020,5,28,310822536,343441509,true +2020,5,28,867225964,61036026,false +2020,5,28,719711891,1269441223,true +2020,5,28,621895191,1113835240,true +2020,5,28,378026525,1108388303,false +2020,5,29,1494959982,1501885867,false +2020,5,29,696730623,1360140818,false +2020,5,29,184257405,9205490,false +2020,5,29,1518824895,553634125,false +2020,5,29,13926550,1684386609,true +2020,5,29,525999711,1364854467,true +2020,5,29,1977986508,88792733,true +2020,5,29,1713334652,959837086,false +2020,5,29,1375275660,105807929,false +2020,5,29,934864050,456160114,true +2020,5,30,1325470809,1427543945,false +2020,5,30,2024341686,895392631,false +2020,5,30,335704528,677753785,true +2020,5,30,1119441073,362779416,false +2020,5,30,1656938615,2099345509,true +2020,5,30,1648006588,760546024,false +2020,5,30,1031274255,810675015,false +2020,5,30,1778939275,756614939,false +2020,5,30,1156888668,1220791146,false +2020,5,30,1168853674,1916121358,true +2020,5,31,1714467493,996273596,true +2020,5,31,765512158,910198678,false +2020,5,31,1290192465,1922530325,true +2020,5,31,587340675,720484347,false +2020,5,31,1760963186,191744778,true +2020,5,31,2044645625,1880349166,false +2020,5,31,1811238943,1089771556,true +2020,5,31,1913691532,1626265315,false +2020,5,31,319315426,1609610586,false +2020,5,31,1343635281,1954726290,false +2020,6,1,130183862,2041757652,false +2020,6,1,1907122156,538035336,true +2020,6,1,129915960,578965353,true +2020,6,1,1407366722,97113874,false +2020,6,1,1432276511,1652580697,true +2020,6,1,843655553,1557016040,false +2020,6,1,763918619,1037659173,false +2020,6,1,558134023,1445949795,false +2020,6,1,325201316,1426709406,false +2020,6,1,400658437,1371682085,false +2020,6,2,774673251,186743683,true +2020,6,2,2104134908,1767509361,false +2020,6,2,451377213,533718079,true +2020,6,2,1617077347,624524468,true +2020,6,2,410346021,401023140,false +2020,6,2,1025089299,815521767,true +2020,6,2,180188478,825085123,false +2020,6,2,779268179,569772746,false +2020,6,2,1006236669,947367208,false +2020,6,2,2066151572,1618967867,false +2020,6,3,1361588544,820871966,false +2020,6,3,766132540,772509956,true +2020,6,3,1247464603,1015184850,true +2020,6,3,1724231961,2078131197,false +2020,6,3,1602555725,1629514087,true +2020,6,3,1517419595,45343830,true +2020,6,3,1922452625,2136634461,true +2020,6,3,1604598729,571307360,false +2020,6,3,1011208784,671703255,false +2020,6,3,99547088,1493807414,true +2020,6,4,86396764,2141260446,false +2020,6,4,878073300,814819338,true +2020,6,4,457517755,636855298,false +2020,6,4,1257936345,902346761,true +2020,6,4,548744112,883669723,true +2020,6,4,1904241712,1195933977,false +2020,6,4,1454478458,1915702361,false +2020,6,4,1163009041,1909530062,false +2020,6,4,27296265,1071102315,true +2020,6,4,753849491,785768310,true +2020,6,5,334598425,620601055,false +2020,6,5,624264411,1383439626,true +2020,6,5,1349813615,511654799,false +2020,6,5,1517581744,635561310,true +2020,6,5,473336369,1764523051,true +2020,6,5,1490306475,198353651,false +2020,6,5,1598728841,597608335,true +2020,6,5,1616493237,1097501742,true +2020,6,5,1345447960,18190239,false +2020,6,5,1600784036,2093917409,false +2020,6,6,1103292645,934300185,false +2020,6,6,160296453,278638190,false +2020,6,6,97134793,685971943,true +2020,6,6,1359775608,1077033457,true +2020,6,6,1365703303,1595206870,true +2020,6,6,1945261875,1007367202,false +2020,6,6,1040450321,352154293,false +2020,6,6,1462185340,1153484027,true +2020,6,6,787170736,81128014,true +2020,6,6,1681373247,886787405,false +2020,6,7,886432252,529112507,false +2020,6,7,1438119690,1067498167,true +2020,6,7,36789488,1842338558,true +2020,6,7,1310983700,2021222086,true +2020,6,7,1552565376,231771882,false +2020,6,7,836030492,1156894763,false +2020,6,7,1701593455,1891841976,true +2020,6,7,183466050,1538311487,true +2020,6,7,1995427176,1301543401,true +2020,6,7,1208976423,981835096,false +2020,6,8,2106512248,1065880891,false +2020,6,8,1862093816,299102867,false +2020,6,8,270274435,503236824,false +2020,6,8,1379518773,1409646629,true +2020,6,8,562109767,1442979091,false +2020,6,8,1472583238,1025010000,true +2020,6,8,221798925,1598330438,false +2020,6,8,957489222,1208929163,true +2020,6,8,616129557,1638786995,true +2020,6,8,563397009,841230981,false +2020,6,9,99502429,845465201,false +2020,6,9,1623557864,1824427779,false +2020,6,9,1459759969,32922576,true +2020,6,9,1443336790,1417488846,true +2020,6,9,2094219217,925047741,false +2020,6,9,674313749,855409197,true +2020,6,9,883857024,859186945,true +2020,6,9,121491235,306625437,false +2020,6,9,1391061751,2086724215,false +2020,6,9,262859341,1549459650,true +2020,6,10,646108264,496427924,false +2020,6,10,1135437779,1036087755,true +2020,6,10,540084477,634851275,true +2020,6,10,1175286004,1765753532,true +2020,6,10,1231454649,724279219,false +2020,6,10,181090313,1888883295,true +2020,6,10,1468441179,1157820419,true +2020,6,10,1729787630,1207418052,false +2020,6,10,701705485,2129361336,true +2020,6,10,913359088,307532957,true +2020,6,11,991972287,1763983362,true +2020,6,11,909235321,968742108,true +2020,6,11,1665409,768888270,false +2020,6,11,257783345,1056723027,false +2020,6,11,1757381690,1454689793,true +2020,6,11,19518027,884152142,false +2020,6,11,122362756,1975300297,false +2020,6,11,1067661877,102419296,true +2020,6,11,1977318313,554106249,true +2020,6,11,33235949,299456468,true +2020,6,12,1796860896,1467462727,false +2020,6,12,729474461,491966853,false +2020,6,12,1282919781,1543097008,true +2020,6,12,808883030,1881860873,true +2020,6,12,1468337415,757346311,true +2020,6,12,1663497727,606978673,true +2020,6,12,1569323823,925771928,true +2020,6,12,504994251,176514064,true +2020,6,12,1913241247,118787209,false +2020,6,12,1987035106,1630880263,false +2020,6,13,1038464086,1529009957,true +2020,6,13,366109174,22258307,true +2020,6,13,1567505232,988498664,true +2020,6,13,1730527228,62633714,true +2020,6,13,1525298474,978390014,false +2020,6,13,190508870,1258962079,false +2020,6,13,2037316813,1318307316,true +2020,6,13,1530128299,1890468716,true +2020,6,13,1826179750,2014677703,true +2020,6,13,864446087,735809965,false +2020,6,14,1557299745,1827006612,true +2020,6,14,1801781425,495658059,false +2020,6,14,2048575065,1813973148,false +2020,6,14,984265665,365367476,true +2020,6,14,1156580412,1095161179,false +2020,6,14,156533813,291742285,false +2020,6,14,1089826454,1585706706,true +2020,6,14,2046560598,177307485,true +2020,6,14,2143929534,1404549992,false +2020,6,14,1481933046,317260197,false +2020,6,15,1622718173,902238442,true +2020,6,15,1937175639,965365030,false +2020,6,15,2008802051,1690260611,true +2020,6,15,2040491218,36182982,false +2020,6,15,405851273,674449877,true +2020,6,15,1314456103,1468284619,true +2020,6,15,934077295,1907476597,true +2020,6,15,255196535,923242351,false +2020,6,15,1774764986,1683289199,true +2020,6,15,182924115,542506482,false +2020,6,16,1592087804,1263399336,false +2020,6,16,294544488,1092137720,false +2020,6,16,1043656837,1009730358,true +2020,6,16,1759295408,584145174,true +2020,6,16,1517830401,1404452609,false +2020,6,16,1442548275,68400347,false +2020,6,16,287422555,1935833931,false +2020,6,16,1100197814,1252409270,false +2020,6,16,1370902853,448179962,true +2020,6,16,1034953969,1152095878,false +2020,6,17,2020698040,84440748,false +2020,6,17,1393356573,2069415615,true +2020,6,17,1774311492,827367427,true +2020,6,17,1119536639,1434905952,false +2020,6,17,575526925,1239044527,true +2020,6,17,480967863,881836342,true +2020,6,17,1816274880,1961390618,true +2020,6,17,1619868411,710317594,true +2020,6,17,955838959,1971019714,true +2020,6,17,654297997,1724627242,true +2020,6,18,1957224844,7353769,false +2020,6,18,1808981505,1232848440,true +2020,6,18,18438017,566936417,false +2020,6,18,112453948,1749832921,false +2020,6,18,951856789,1673913248,false +2020,6,18,818327847,1907566559,true +2020,6,18,1484702495,605963609,false +2020,6,18,76619153,2026664994,false +2020,6,18,1650788402,812833491,true +2020,6,18,1529045303,185265121,true +2020,6,19,1547953740,1636190011,false +2020,6,19,1935259492,1611371051,false +2020,6,19,72941486,206413462,false +2020,6,19,376212397,1195769351,true +2020,6,19,503960798,1341386019,false +2020,6,19,2089878194,1662334803,true +2020,6,19,660287506,2137920151,true +2020,6,19,871490544,2146879390,false +2020,6,19,755714152,1905253035,false +2020,6,19,686859262,1327335259,false +2020,6,20,482362936,145731579,true +2020,6,20,1154094036,642788421,true +2020,6,20,1496598505,1627463467,false +2020,6,20,1966724330,2061252560,true +2020,6,20,1927138061,333737917,true +2020,6,20,610038608,1680583925,false +2020,6,20,277151667,403778867,true +2020,6,20,832618001,2026732220,false +2020,6,20,190127142,1545496131,false +2020,6,20,858125180,894426449,false +2020,6,21,1779226014,1900214602,true +2020,6,21,1182493224,1776986509,false +2020,6,21,1207877255,878005314,false +2020,6,21,477550798,1171953456,false +2020,6,21,1241318765,400071656,true +2020,6,21,1298468437,1635957830,true +2020,6,21,333117359,2120773844,false +2020,6,21,875831446,890673257,true +2020,6,21,1072714112,463737521,true +2020,6,21,2146018254,337434852,false +2020,6,22,806934760,492097752,false +2020,6,22,1584442807,355759839,false +2020,6,22,399479740,1042087156,false +2020,6,22,223153846,1694336787,false +2020,6,22,1144858829,11161482,true +2020,6,22,1094162600,647698124,false +2020,6,22,519125688,936275813,false +2020,6,22,1968983604,608261139,true +2020,6,22,1240913692,370822242,true +2020,6,22,1281506987,319557458,true +2020,6,23,1987313450,797260566,true +2020,6,23,128872918,314962661,false +2020,6,23,1132390079,1202000433,false +2020,6,23,454159436,1877780800,true +2020,6,23,1503878545,1257784251,true +2020,6,23,1385058341,898442426,true +2020,6,23,1690171925,2058541946,false +2020,6,23,1637760797,1508140065,false +2020,6,23,2007932963,573686047,false +2020,6,23,1412774537,539162013,true +2020,6,24,1176104326,1267230735,true +2020,6,24,683580080,1156459915,true +2020,6,24,593593593,1326024674,true +2020,6,24,290073183,689641823,true +2020,6,24,436350022,38166509,true +2020,6,24,111561941,87144102,true +2020,6,24,1981795449,1204139381,false +2020,6,24,905978713,1160737987,false +2020,6,24,508668777,943587162,true +2020,6,24,2012757269,1824429051,true +2020,6,25,761478149,45636440,false +2020,6,25,1089586965,1764499179,false +2020,6,25,1990833960,253544508,true +2020,6,25,1746944879,450040704,false +2020,6,25,665182434,2007893394,true +2020,6,25,154621441,2028168963,false +2020,6,25,1195441778,746861756,true +2020,6,25,2047181394,687142113,false +2020,6,25,1731764075,1048197233,true +2020,6,25,426744175,639543852,false +2020,6,26,914786487,1431414555,false +2020,6,26,711614428,1393192976,false +2020,6,26,1665686135,790905220,true +2020,6,26,818639604,908527019,false +2020,6,26,162038257,828773076,false +2020,6,26,65796689,135673359,false +2020,6,26,2038578951,1265104854,false +2020,6,26,1457458439,2069049683,true +2020,6,26,1657473267,476322547,true +2020,6,26,1017918430,729372669,false +2020,6,27,1503789109,967526253,false +2020,6,27,1615522006,1111887408,false +2020,6,27,1908632567,1457430965,true +2020,6,27,1049014671,1223311456,true +2020,6,27,1744506713,1920115714,false +2020,6,27,1450853739,1685304694,true +2020,6,27,1203797858,1326515554,true +2020,6,27,797075854,1508387506,false +2020,6,27,16784510,1241759490,false +2020,6,27,888904704,1398346665,true +2020,6,28,1034484442,766871143,true +2020,6,28,272538782,680969518,false +2020,6,28,908749572,1804016837,false +2020,6,28,1620782495,2111069198,true +2020,6,28,845482123,1948619447,true +2020,6,28,1285773015,870181084,true +2020,6,28,279758486,2027316493,true +2020,6,28,547806718,1937432657,true +2020,6,28,872444597,434303588,true +2020,6,28,526570404,27628507,false +2020,6,29,1884562511,2105445058,true +2020,6,29,1994250138,887298167,true +2020,6,29,987175802,171446649,true +2020,6,29,1041916154,2018415087,true +2020,6,29,421149736,59909869,true +2020,6,29,801486098,333440775,false +2020,6,29,1930275559,872997658,true +2020,6,29,1499373019,106725195,true +2020,6,29,410822310,16360179,false +2020,6,29,1622289350,1339843957,false +2020,6,30,213163840,393550303,true +2020,6,30,1559769098,417730977,false +2020,6,30,283462337,1128009235,true +2020,6,30,872117169,1898300623,true +2020,6,30,1079782289,600813934,false +2020,6,30,1883983120,487379815,false +2020,6,30,764880919,293911073,false +2020,6,30,199944323,238751403,false +2020,6,30,584290418,233768280,true +2020,6,30,1083552368,682999147,false +2020,6,31,283429202,674167685,true +2020,6,31,1393336322,812700240,true +2020,6,31,831445786,137621813,false +2020,6,31,2107369767,2034260634,true +2020,6,31,1362356951,161654895,false +2020,6,31,706212259,362351140,true +2020,6,31,1207580328,1813886466,false +2020,6,31,1623715943,1984586253,false +2020,6,31,1941709232,352924508,false +2020,6,31,1774201700,2066597206,false +2020,7,1,790248365,638820539,true +2020,7,1,1843440532,2028598101,true +2020,7,1,1984204476,1872526543,false +2020,7,1,1052372973,1761042893,true +2020,7,1,1652353534,1081912884,true +2020,7,1,245542782,259055880,false +2020,7,1,531032598,944093256,true +2020,7,1,425261543,1435329104,false +2020,7,1,1374632120,814262333,false +2020,7,1,115362529,285193612,false +2020,7,2,30175939,976486537,true +2020,7,2,362627228,1252207863,true +2020,7,2,484927636,1853192183,true +2020,7,2,816309787,2100256292,true +2020,7,2,1802735263,20936458,true +2020,7,2,899004880,2047884753,false +2020,7,2,1968997485,299805041,false +2020,7,2,1048909357,824485944,true +2020,7,2,1553147529,1301744313,false +2020,7,2,1407857887,590191063,true +2020,7,3,1645722639,1107582087,true +2020,7,3,276954868,1857617130,true +2020,7,3,1373833934,43794934,true +2020,7,3,543492888,716738065,true +2020,7,3,111969603,657596155,false +2020,7,3,911990952,2051540694,false +2020,7,3,590277588,1110549455,true +2020,7,3,163094873,1512900359,false +2020,7,3,1601220376,113601492,true +2020,7,3,564587670,1247948328,true +2020,7,4,14922504,22912121,true +2020,7,4,1874589420,649596011,true +2020,7,4,608555891,1678167031,true +2020,7,4,77774106,1885345509,true +2020,7,4,1744652298,1883044111,true +2020,7,4,1429519777,2008886532,false +2020,7,4,1409929946,1985467397,true +2020,7,4,2038566112,870026610,false +2020,7,4,1146949143,353719950,true +2020,7,4,20756268,1236264670,true +2020,7,5,2119452285,1315142517,false +2020,7,5,19237212,1940295018,false +2020,7,5,1491051959,1546199057,false +2020,7,5,1268995009,1744066480,true +2020,7,5,1610814598,1766164722,true +2020,7,5,990043271,121196190,true +2020,7,5,1451409063,452720713,true +2020,7,5,1587269102,1205387662,true +2020,7,5,591434403,927010806,false +2020,7,5,291916252,1241285741,true +2020,7,6,282199561,2034215488,true +2020,7,6,2010333112,1405333456,false +2020,7,6,1264706257,1155349415,true +2020,7,6,2024700562,277691206,true +2020,7,6,1105493195,1390869839,false +2020,7,6,217148665,11061314,true +2020,7,6,1920759402,2119885554,true +2020,7,6,1825527067,1432092794,true +2020,7,6,1806811733,1226639453,false +2020,7,6,1355431674,888706190,true +2020,7,7,1478353778,665625062,true +2020,7,7,160562141,1746485320,false +2020,7,7,1001463671,1247600730,false +2020,7,7,275066189,2144194534,false +2020,7,7,1483307039,330200147,true +2020,7,7,116215600,1081935395,false +2020,7,7,1489657233,668909051,true +2020,7,7,381358061,357893366,false +2020,7,7,658716839,1435045283,false +2020,7,7,2125696664,1398836038,false +2020,7,8,786609668,1061484215,false +2020,7,8,350361240,2056357897,false +2020,7,8,912274254,1144547832,true +2020,7,8,988738884,1863597947,false +2020,7,8,1727948250,371483907,false +2020,7,8,127066475,1257576826,false +2020,7,8,89151151,1532253320,false +2020,7,8,1340108231,1537030184,false +2020,7,8,457814450,1626414026,false +2020,7,8,1270937280,1711127646,false +2020,7,9,1914582602,851238709,false +2020,7,9,2028517654,1532047771,true +2020,7,9,463627758,188842287,false +2020,7,9,1470894661,2121764208,false +2020,7,9,372599119,1374422897,false +2020,7,9,2012587202,1918930584,true +2020,7,9,1840062239,1266625671,false +2020,7,9,630655326,676767293,true +2020,7,9,404778922,682165328,false +2020,7,9,312093815,1048707616,false +2020,7,10,325052081,1262363537,false +2020,7,10,1316241597,1503615121,false +2020,7,10,1436240207,250673646,true +2020,7,10,302097076,2103693187,false +2020,7,10,276164563,1188057848,false +2020,7,10,513208127,1723944481,true +2020,7,10,1152396232,777784836,false +2020,7,10,1521695948,1862454440,false +2020,7,10,1924563114,1703173643,false +2020,7,10,1377020425,1734629498,false +2020,7,11,878182908,1901402566,true +2020,7,11,433888798,2060423836,true +2020,7,11,647713718,932454843,false +2020,7,11,1530000867,103546153,true +2020,7,11,665363010,358970624,false +2020,7,11,383205009,1105102938,false +2020,7,11,1240530308,1649968854,false +2020,7,11,274979284,1890126380,false +2020,7,11,1211557542,1323144817,true +2020,7,11,1008060499,528462810,true +2020,7,12,751182486,1311080323,false +2020,7,12,1374138112,617528985,true +2020,7,12,1866142587,1384547687,true +2020,7,12,1239140563,2059894109,false +2020,7,12,1185188240,1048202238,false +2020,7,12,1321445823,2064339854,true +2020,7,12,125450273,1460099227,false +2020,7,12,575885563,1493815543,false +2020,7,12,518084004,1412915861,true +2020,7,12,913430526,400159065,false +2020,7,13,909604377,814038607,false +2020,7,13,419520691,1766672989,true +2020,7,13,1527772479,629970658,false +2020,7,13,1033277658,25582824,false +2020,7,13,1706842695,117164454,false +2020,7,13,1652767123,2106349297,true +2020,7,13,982356213,126835787,true +2020,7,13,1236963706,1462298994,true +2020,7,13,983544966,136216900,true +2020,7,13,69465320,1047951884,false +2020,7,14,1777403563,1696743351,true +2020,7,14,2004697901,148601447,false +2020,7,14,1544984946,159640359,false +2020,7,14,679147753,730603403,true +2020,7,14,153992452,894170258,true +2020,7,14,424339141,923705750,false +2020,7,14,562788977,1151701444,false +2020,7,14,1445013819,1780721821,false +2020,7,14,61201165,1401786628,false +2020,7,14,395682100,1408771486,false +2020,7,15,1924366340,354926182,true +2020,7,15,1524887203,1818664651,false +2020,7,15,109228076,1182061933,true +2020,7,15,404727141,1586658455,true +2020,7,15,1705153101,270460099,false +2020,7,15,401764874,1809987913,true +2020,7,15,1471174726,1590729509,false +2020,7,15,223344476,919537409,false +2020,7,15,1177730017,166198240,true +2020,7,15,1569892023,337551484,false +2020,7,16,2064377905,601015922,true +2020,7,16,1376945297,246617837,false +2020,7,16,1035195087,216285223,true +2020,7,16,1849229256,1997839308,false +2020,7,16,737259097,692869066,true +2020,7,16,128421257,1482297195,true +2020,7,16,1065653233,926948038,true +2020,7,16,749137138,428447147,false +2020,7,16,2074301366,1145927754,true +2020,7,16,1246076087,226830387,false +2020,7,17,1362009202,102771274,false +2020,7,17,507083386,378937830,false +2020,7,17,1899227373,51158390,true +2020,7,17,2093778578,515613079,false +2020,7,17,1227025824,1381935229,false +2020,7,17,1561505907,975401516,true +2020,7,17,1913269189,1582112820,false +2020,7,17,1547644663,1581544616,true +2020,7,17,1887758069,2075044221,true +2020,7,17,75861305,541091036,true +2020,7,18,1792512206,802087683,true +2020,7,18,1988956097,45403048,true +2020,7,18,953016928,1002565662,false +2020,7,18,409319214,755964436,true +2020,7,18,475848013,299754368,true +2020,7,18,916700327,1571606193,true +2020,7,18,1069276889,1630622593,true +2020,7,18,215040437,1157118711,false +2020,7,18,1438342506,1126290203,true +2020,7,18,1728831574,568758435,true +2020,7,19,1764877604,1118237173,false +2020,7,19,1794908524,1896439614,true +2020,7,19,329515677,1989350166,false +2020,7,19,1035759311,1613834323,false +2020,7,19,1370559205,1399106830,true +2020,7,19,934423470,1579695362,true +2020,7,19,146011734,1832628159,false +2020,7,19,1553074435,429146610,true +2020,7,19,454916069,469281540,false +2020,7,19,887995720,1632931529,true +2020,7,20,603483372,1164457710,false +2020,7,20,1396166061,233223355,true +2020,7,20,450507172,1778702671,true +2020,7,20,1883661434,1171184591,false +2020,7,20,791266505,1550157250,true +2020,7,20,1988760431,2111629537,true +2020,7,20,707555615,1101900928,true +2020,7,20,1063004722,904040086,true +2020,7,20,317734355,1545464216,true +2020,7,20,668125232,1849301432,true +2020,7,21,1652905699,667916642,true +2020,7,21,1627477636,176555441,false +2020,7,21,2092094749,1478045772,false +2020,7,21,15888821,597803042,true +2020,7,21,1829687635,1723990865,false +2020,7,21,1627245678,1665370828,true +2020,7,21,1531646159,349209524,false +2020,7,21,1524108159,1673834813,true +2020,7,21,1910499638,1567726554,false +2020,7,21,471619786,757750784,true +2020,7,22,247384147,1949314137,false +2020,7,22,383073764,1838136651,true +2020,7,22,1037117573,456880020,true +2020,7,22,1040421011,1998533462,true +2020,7,22,1729311938,1640404857,true +2020,7,22,1118022257,1054333666,false +2020,7,22,810157939,527244350,true +2020,7,22,1231624796,1242036378,false +2020,7,22,531289486,734084154,true +2020,7,22,529746303,1593977429,false +2020,7,23,583830461,585493530,false +2020,7,23,146243342,238979426,true +2020,7,23,1806300529,1253391408,true +2020,7,23,1217991956,194272610,true +2020,7,23,616334899,843567754,true +2020,7,23,545512930,464700254,false +2020,7,23,886299541,1552603921,true +2020,7,23,1712356605,1639206685,false +2020,7,23,1696864903,1622574569,false +2020,7,23,963780511,1500265698,false +2020,7,24,1031075077,1308109239,true +2020,7,24,1245274153,1453578268,true +2020,7,24,1950842198,193868459,false +2020,7,24,1134789909,703616945,false +2020,7,24,259818068,2097709143,false +2020,7,24,1812957000,1836591844,true +2020,7,24,234230901,268600248,true +2020,7,24,2110041305,1454479884,true +2020,7,24,992122252,1165730816,false +2020,7,24,1883757732,1374307476,true +2020,7,25,1844405629,964295685,false +2020,7,25,344288322,861501948,true +2020,7,25,1587375193,9483096,true +2020,7,25,2001395007,459816401,false +2020,7,25,1202093722,1408818548,true +2020,7,25,536221604,1823868636,false +2020,7,25,1388016247,1146500754,false +2020,7,25,1680539438,147163122,true +2020,7,25,74368400,776607983,false +2020,7,25,1513297356,1956191530,true +2020,7,26,2113239129,773451443,true +2020,7,26,689506614,541745772,false +2020,7,26,2095667470,1829503936,false +2020,7,26,1630591676,1984368783,true +2020,7,26,485943835,1867530246,true +2020,7,26,989304741,833688704,false +2020,7,26,1050832952,261738035,true +2020,7,26,60794281,1550776364,false +2020,7,26,1961890475,931419100,false +2020,7,26,1017793138,874100515,false +2020,7,27,1131570287,591001455,false +2020,7,27,838835762,72288551,false +2020,7,27,495499059,399986057,false +2020,7,27,1423202456,560175917,true +2020,7,27,665211236,1162164880,true +2020,7,27,5975266,2145102710,true +2020,7,27,1714919113,545664668,false +2020,7,27,1261274288,250451911,true +2020,7,27,745568093,727757066,false +2020,7,27,1576446696,750793964,true +2020,7,28,1633524314,1578845790,false +2020,7,28,1319349966,1908198671,false +2020,7,28,535350214,380184122,true +2020,7,28,232375573,1042052738,true +2020,7,28,923386068,2107197582,true +2020,7,28,812285854,1572054797,true +2020,7,28,2033869819,2097993806,true +2020,7,28,547892759,624592871,false +2020,7,28,1979519892,1414752090,true +2020,7,28,2065819839,353971419,true +2020,7,29,549103189,810295895,false +2020,7,29,1963752436,2019327306,false +2020,7,29,1671704335,1149701281,true +2020,7,29,1570731614,1668474895,true +2020,7,29,1859349875,896584620,false +2020,7,29,859917514,678556519,true +2020,7,29,260451268,174657128,true +2020,7,29,669363735,1289875565,true +2020,7,29,203524554,1866397826,true +2020,7,29,155848390,222404048,false +2020,7,30,1012855649,1366391451,true +2020,7,30,330299144,1712187339,true +2020,7,30,388844912,1131542474,false +2020,7,30,862848866,1033585534,true +2020,7,30,620580405,243684870,true +2020,7,30,972080952,1349448260,false +2020,7,30,49483301,1254255178,false +2020,7,30,1187345891,1824183704,false +2020,7,30,625685718,1685514898,false +2020,7,30,552800807,129627809,false +2020,7,31,726518179,804945663,false +2020,7,31,1298263314,1768262787,true +2020,7,31,1424044563,1279688900,true +2020,7,31,1824470198,1603318532,true +2020,7,31,1937659944,671105202,false +2020,7,31,873480250,347485995,true +2020,7,31,1751900445,894345331,true +2020,7,31,1513019961,1218539991,false +2020,7,31,633198913,1694314713,true +2020,7,31,1411620707,407251088,false +2020,8,1,104649713,1233532106,false +2020,8,1,906139354,39348981,false +2020,8,1,592070361,1323674544,true +2020,8,1,661044941,463761299,false +2020,8,1,954101256,1266429822,true +2020,8,1,428815153,510074134,false +2020,8,1,417880706,1557778840,true +2020,8,1,1819878832,840779011,false +2020,8,1,1957461827,219252560,true +2020,8,1,2138563874,1442065281,false +2020,8,2,1588474114,443347376,true +2020,8,2,1170473010,435149795,false +2020,8,2,730442513,318221766,true +2020,8,2,430305783,1501596653,false +2020,8,2,1498306593,588231090,false +2020,8,2,538120361,1064894463,true +2020,8,2,671868899,11393061,false +2020,8,2,260404670,2092241079,true +2020,8,2,961229490,1508142864,false +2020,8,2,2130418706,1204629167,true +2020,8,3,673751762,644396979,true +2020,8,3,96619492,1922931531,false +2020,8,3,1471602400,107779407,false +2020,8,3,399797713,1492981226,false +2020,8,3,1120097166,1564310827,false +2020,8,3,412046611,362119885,true +2020,8,3,1655442108,738256292,true +2020,8,3,1867992747,17540494,false +2020,8,3,734497930,22168449,true +2020,8,3,338531913,1418918663,true +2020,8,4,734028548,1277553613,true +2020,8,4,1565887233,1102388974,true +2020,8,4,334091110,620445144,true +2020,8,4,2117070137,97879960,false +2020,8,4,43092021,1741233201,false +2020,8,4,1819698255,348835485,true +2020,8,4,604556701,1036862656,true +2020,8,4,1914394087,1232979802,false +2020,8,4,1266267603,445273915,false +2020,8,4,1560250853,223829114,false +2020,8,5,1957858672,1561042612,true +2020,8,5,1858272639,1568875540,false +2020,8,5,1488029688,1789447668,false +2020,8,5,409793765,1396432381,true +2020,8,5,818416402,716185556,true +2020,8,5,878354038,1544958561,false +2020,8,5,1088981787,825852896,true +2020,8,5,1935590269,197007041,true +2020,8,5,719001109,484572384,true +2020,8,5,1995497956,906284512,false +2020,8,6,2079228451,1580788251,true +2020,8,6,593555301,802460498,true +2020,8,6,938560276,1749085557,false +2020,8,6,1662435872,1080507806,false +2020,8,6,1520668332,1544572851,true +2020,8,6,428473243,1662880312,false +2020,8,6,1410390435,657947019,true +2020,8,6,875047581,402386112,false +2020,8,6,223192985,998974280,true +2020,8,6,1763898718,177844036,true +2020,8,7,1839343220,1389399598,false +2020,8,7,305484037,828474229,false +2020,8,7,1491470811,1563902434,false +2020,8,7,1169974377,1253860634,true +2020,8,7,1082365387,361870716,true +2020,8,7,1110672276,1008036892,false +2020,8,7,53579648,2097598227,false +2020,8,7,1434519154,1197930751,false +2020,8,7,1089145380,1406963168,false +2020,8,7,1260349622,587141297,false +2020,8,8,1958942975,1375818318,true +2020,8,8,1317040611,942986469,true +2020,8,8,488301346,748117134,true +2020,8,8,1868164069,1486773374,true +2020,8,8,1503622339,321197447,false +2020,8,8,1867578327,1180357557,false +2020,8,8,63342780,999675710,false +2020,8,8,1476678589,854478335,false +2020,8,8,1398698230,1435042059,false +2020,8,8,269057367,159654876,true +2020,8,9,1610927779,883982637,true +2020,8,9,699874657,603104534,false +2020,8,9,1955922831,1268412577,true +2020,8,9,393270229,348873753,true +2020,8,9,589085686,99235665,true +2020,8,9,1138981138,1677326704,false +2020,8,9,55620767,1530510819,true +2020,8,9,1016740074,1181673094,false +2020,8,9,345211690,800114652,true +2020,8,9,2008744972,1109116215,false +2020,8,10,1505015512,1841003479,true +2020,8,10,1849347404,1248544228,true +2020,8,10,1508423219,1735307507,true +2020,8,10,501980069,1789145081,false +2020,8,10,110975582,1790156753,true +2020,8,10,571863827,2080356651,true +2020,8,10,1704985336,2039046832,true +2020,8,10,620471713,2050336217,true +2020,8,10,382649811,1512772493,false +2020,8,10,1865110318,2055459154,true +2020,8,11,2027291638,801677398,false +2020,8,11,1107126744,598378229,false +2020,8,11,669000094,230124680,false +2020,8,11,1209707040,1965460828,true +2020,8,11,535294259,1329650900,true +2020,8,11,466344204,1315928855,false +2020,8,11,856465787,2132788383,true +2020,8,11,666286079,1835724964,true +2020,8,11,569130342,2020847228,false +2020,8,11,325016836,929641887,false +2020,8,12,1838684232,1611198444,false +2020,8,12,457306031,733906794,false +2020,8,12,1478257509,1554953733,false +2020,8,12,725642733,1740551600,true +2020,8,12,1757741583,1842252764,true +2020,8,12,2072822106,2056947101,true +2020,8,12,907978703,939693247,false +2020,8,12,972089586,1867730587,true +2020,8,12,1790324815,1215669141,true +2020,8,12,1667669044,1031826411,true +2020,8,13,1788343035,1320734229,false +2020,8,13,949249049,882508937,false +2020,8,13,507413648,1355343239,true +2020,8,13,768082797,1477402375,false +2020,8,13,685106976,725135432,false +2020,8,13,225114977,1559628140,true +2020,8,13,1876256039,1903011409,true +2020,8,13,67981943,2024250682,false +2020,8,13,754017989,2098241642,false +2020,8,13,303649562,542271015,false +2020,8,14,602331205,1851534551,false +2020,8,14,1673989242,131448365,false +2020,8,14,2076819308,379217306,true +2020,8,14,1836829573,607984636,true +2020,8,14,1019375943,517800865,false +2020,8,14,2135806741,877635830,true +2020,8,14,1675758714,195972991,false +2020,8,14,1226973612,1725975095,true +2020,8,14,650411065,1161841526,false +2020,8,14,195911766,954821729,false +2020,8,15,1270933098,743592966,false +2020,8,15,1355544963,82715674,true +2020,8,15,1178145708,2117290809,false +2020,8,15,1692082172,550528384,true +2020,8,15,1647618611,1770487904,false +2020,8,15,529927488,1060815624,false +2020,8,15,103353530,757315844,false +2020,8,15,2110407660,1959747702,true +2020,8,15,353166583,1669945654,true +2020,8,15,1480261112,563159285,false +2020,8,16,1878844759,80181379,false +2020,8,16,1074294770,211722112,true +2020,8,16,531869678,1329595951,true +2020,8,16,890896764,1687078669,false +2020,8,16,1407852762,937609875,false +2020,8,16,1432574137,1334768709,true +2020,8,16,752355473,1200579749,false +2020,8,16,786491762,2084306084,false +2020,8,16,1068744395,1611016731,false +2020,8,16,954790413,1115117407,true +2020,8,17,424440074,1439849891,true +2020,8,17,922632118,1827439198,true +2020,8,17,916895728,1534612010,true +2020,8,17,1520502863,66103849,false +2020,8,17,1520133340,913513251,false +2020,8,17,1923314675,871835040,true +2020,8,17,153595282,1645750610,false +2020,8,17,1978163513,635145932,false +2020,8,17,1709010383,1785591942,false +2020,8,17,1700681342,1514190902,false +2020,8,18,769603349,804621892,false +2020,8,18,1822080981,1915257477,false +2020,8,18,55764255,803621460,true +2020,8,18,1282169971,934361158,true +2020,8,18,1758886328,888095148,false +2020,8,18,1551525816,186820153,false +2020,8,18,493202662,1242126645,false +2020,8,18,1193371923,1756378229,true +2020,8,18,1474688681,1208969551,true +2020,8,18,666958723,1532092731,false +2020,8,19,1984261574,915221552,true +2020,8,19,1112479837,1710733400,true +2020,8,19,234142541,1961322596,false +2020,8,19,415158939,2024360540,true +2020,8,19,2055939291,1281752610,false +2020,8,19,1707656939,1269517597,true +2020,8,19,1374576589,1578043696,true +2020,8,19,170074664,1885537077,false +2020,8,19,561450464,1595308674,false +2020,8,19,690737935,1793776251,false +2020,8,20,1498054629,594162661,true +2020,8,20,1429771596,115536305,true +2020,8,20,11416992,461643092,true +2020,8,20,1631245102,1364161495,true +2020,8,20,448829929,976465596,true +2020,8,20,340734117,1602094046,false +2020,8,20,1109082272,2042253930,true +2020,8,20,1855831388,539123128,true +2020,8,20,848265241,692233041,true +2020,8,20,1156947206,1968433653,false +2020,8,21,1795174922,29634531,false +2020,8,21,452475961,1106040685,true +2020,8,21,148359206,1523836223,true +2020,8,21,1523135857,1992651431,false +2020,8,21,194854621,1274931165,true +2020,8,21,1061334284,1014592764,true +2020,8,21,707585285,1603220989,false +2020,8,21,331707481,1119483306,true +2020,8,21,995293524,1043615102,true +2020,8,21,527008542,1204163919,true +2020,8,22,1693505267,30147063,true +2020,8,22,693672982,941258885,false +2020,8,22,1041522836,299501859,true +2020,8,22,1353321560,1140310170,true +2020,8,22,160079305,1300255472,true +2020,8,22,2036119209,652835224,true +2020,8,22,1771544730,1271355366,true +2020,8,22,1395839776,962591504,true +2020,8,22,307222915,1577579416,true +2020,8,22,1051281585,1433046383,false +2020,8,23,993693283,1990357590,true +2020,8,23,1366890864,1649235911,true +2020,8,23,1481524491,2018574441,false +2020,8,23,226710445,891632200,false +2020,8,23,2000825437,1031009906,true +2020,8,23,1102850361,1190615016,true +2020,8,23,1581381450,322331080,true +2020,8,23,1140753268,24296931,true +2020,8,23,1463468814,1733030120,false +2020,8,23,898790903,748151323,false +2020,8,24,422680178,1244130136,false +2020,8,24,346792690,771139978,false +2020,8,24,67656196,1048142220,false +2020,8,24,962317206,1240653362,true +2020,8,24,413264261,1693654177,false +2020,8,24,2046145573,1293844576,false +2020,8,24,1760402684,149903131,true +2020,8,24,1062143620,828817685,false +2020,8,24,357987370,869187298,false +2020,8,24,1301301080,533513198,true +2020,8,25,1941010774,1799880241,false +2020,8,25,684790039,1265046766,false +2020,8,25,2003110065,1173806132,true +2020,8,25,546762142,1843927787,false +2020,8,25,2069495737,1067925384,false +2020,8,25,1242964513,1077168533,true +2020,8,25,2034138212,1148046304,true +2020,8,25,1370940050,1709669964,true +2020,8,25,1055741443,348899699,false +2020,8,25,1895659730,1157781822,false +2020,8,26,2138499601,2132430850,true +2020,8,26,1714506061,1172347020,true +2020,8,26,2022688116,187080858,false +2020,8,26,1977815502,1808403810,false +2020,8,26,1254895317,28967007,false +2020,8,26,1542822056,1565901630,false +2020,8,26,728248603,353801208,true +2020,8,26,352981391,636940267,false +2020,8,26,1030327595,221937283,true +2020,8,26,633410228,1514491600,true +2020,8,27,875724928,262549523,true +2020,8,27,1720049661,2101752995,false +2020,8,27,1542693429,1380134687,true +2020,8,27,1096377206,1208746146,true +2020,8,27,1636944531,698879354,true +2020,8,27,1650929995,1136207583,true +2020,8,27,12589386,944534689,false +2020,8,27,1927135976,298382152,false +2020,8,27,1649036501,1795593860,false +2020,8,27,1420654613,1471670120,false +2020,8,28,1375783360,1255188245,true +2020,8,28,436942381,1314473243,false +2020,8,28,684554949,1288806471,false +2020,8,28,1364491448,346523306,false +2020,8,28,215624443,1093551420,false +2020,8,28,1584253971,402014470,false +2020,8,28,1160819085,2001040087,false +2020,8,28,839456241,780715808,true +2020,8,28,630803126,653925099,true +2020,8,28,17952975,1598090639,true +2020,8,29,2097241070,1156848832,false +2020,8,29,2111782585,1626543103,true +2020,8,29,1254470033,1459837094,true +2020,8,29,77477108,525479460,false +2020,8,29,867144298,1419980230,true +2020,8,29,487523680,1797879842,false +2020,8,29,601874908,610982013,false +2020,8,29,1930372021,1484710568,true +2020,8,29,851084208,362099932,false +2020,8,29,814026729,2086510902,true +2020,8,30,2004704419,1344932903,true +2020,8,30,1373373532,1881387327,true +2020,8,30,111685965,118988131,false +2020,8,30,1412870565,817314599,false +2020,8,30,1969182971,1602048093,false +2020,8,30,760521482,1597793878,true +2020,8,30,2036092048,730515365,true +2020,8,30,2095631891,430190538,false +2020,8,30,307861900,1712890232,false +2020,8,30,30244017,1040652022,true +2020,8,31,64503742,468940945,false +2020,8,31,1135871222,378574540,true +2020,8,31,61017182,790424910,true +2020,8,31,2146495055,716927305,false +2020,8,31,1024503671,45743288,true +2020,8,31,71820216,1588290011,false +2020,8,31,1874469096,1856994889,false +2020,8,31,680077165,582149997,false +2020,8,31,2039677033,768383623,false +2020,8,31,738334719,1963921683,false +2020,9,1,1117978710,1427913674,true +2020,9,1,1318080472,847159418,true +2020,9,1,879734068,556281799,false +2020,9,1,1755176846,247724971,false +2020,9,1,1278088219,1087290301,false +2020,9,1,461942942,1486117839,false +2020,9,1,1971615900,907220562,false +2020,9,1,986394974,2044459233,false +2020,9,1,1734969016,661479805,false +2020,9,1,2102462540,968077985,false +2020,9,2,1209255926,408661016,true +2020,9,2,1956928509,1721595171,false +2020,9,2,211856075,490538475,true +2020,9,2,1293259365,1680434117,true +2020,9,2,1322561996,1827478098,true +2020,9,2,1123816743,731266943,true +2020,9,2,1905816667,501933258,false +2020,9,2,1072167462,300324285,false +2020,9,2,2089913555,1263155205,false +2020,9,2,1813853859,599563085,false +2020,9,3,5271651,405654365,true +2020,9,3,573411449,1195260674,true +2020,9,3,574688016,20423610,true +2020,9,3,202417248,247423515,true +2020,9,3,565438109,1872675907,true +2020,9,3,850682168,658703506,true +2020,9,3,428354886,2085843067,false +2020,9,3,1595217872,663503321,false +2020,9,3,2039943569,452246674,true +2020,9,3,2015167396,494177977,false +2020,9,4,1560386211,868876818,true +2020,9,4,1944571012,1076192070,false +2020,9,4,39360733,565567027,true +2020,9,4,1319125541,2028647460,true +2020,9,4,1925261483,604304510,true +2020,9,4,973883912,878576794,false +2020,9,4,1791474118,682924926,true +2020,9,4,1951200579,498742051,true +2020,9,4,160472932,1515858614,false +2020,9,4,1504495361,322950262,false +2020,9,5,247712256,428714007,false +2020,9,5,703311332,136712511,true +2020,9,5,2056586691,1185308834,false +2020,9,5,607379846,1760631049,false +2020,9,5,1688862907,471292679,true +2020,9,5,1443039944,1888839933,true +2020,9,5,1068106327,2141183062,true +2020,9,5,2074323132,355856259,true +2020,9,5,1028829679,1787442718,true +2020,9,5,289461570,158698992,false +2020,9,6,520821813,1808472721,false +2020,9,6,204288949,1565863871,true +2020,9,6,1549232104,392007304,false +2020,9,6,1460748600,392999685,false +2020,9,6,1595467812,1066701430,true +2020,9,6,484917642,1065599020,false +2020,9,6,1222608661,933401262,false +2020,9,6,1437527134,986283787,true +2020,9,6,1701473801,6810762,true +2020,9,6,184131254,440362492,true +2020,9,7,2122409997,1532176720,true +2020,9,7,900954287,1160788866,false +2020,9,7,455692948,535709934,true +2020,9,7,1801866166,661937553,true +2020,9,7,2233315,399599072,false +2020,9,7,896130605,845580710,true +2020,9,7,1110544684,1227518174,false +2020,9,7,67674336,304564005,true +2020,9,7,241569451,754421282,false +2020,9,7,1192808410,84593083,false +2020,9,8,1389613593,2117275497,false +2020,9,8,967699328,300364728,false +2020,9,8,645520461,2077748481,true +2020,9,8,107794422,1111269530,false +2020,9,8,1303095537,537939315,false +2020,9,8,546455609,1237442111,true +2020,9,8,1593387914,1558638260,true +2020,9,8,1867960186,1610631036,false +2020,9,8,1073301317,1040411880,true +2020,9,8,860847770,85351691,false +2020,9,9,231199564,453067282,false +2020,9,9,1130198229,733385223,false +2020,9,9,798519180,92028739,true +2020,9,9,1940636160,2075187063,false +2020,9,9,1560809011,1725102898,true +2020,9,9,281306598,1587533463,true +2020,9,9,776254544,729387839,true +2020,9,9,1039121324,1012141014,false +2020,9,9,87924829,1809052486,false +2020,9,9,1280192826,1481123023,false +2020,9,10,1855803287,306640347,true +2020,9,10,1002746404,964407709,true +2020,9,10,1633538933,2056839610,true +2020,9,10,1912255112,1211070047,false +2020,9,10,680074901,112943764,false +2020,9,10,1011687887,1140130764,false +2020,9,10,88634622,618654345,true +2020,9,10,373100137,457229847,false +2020,9,10,980782480,361348831,false +2020,9,10,1434073804,1512636322,true +2020,9,11,439239394,331521503,true +2020,9,11,1056547164,1559705901,true +2020,9,11,1639022611,1729969381,true +2020,9,11,1674295315,2111710081,true +2020,9,11,620589622,823567605,false +2020,9,11,627932666,698926795,true +2020,9,11,1243054423,505479554,true +2020,9,11,183421904,1829616159,true +2020,9,11,1263352912,260151337,true +2020,9,11,657348214,2106722781,true +2020,9,12,211339502,359901568,true +2020,9,12,1213471995,1634401775,false +2020,9,12,1638768140,1784396381,false +2020,9,12,133885273,1966024985,true +2020,9,12,21290930,1562675729,true +2020,9,12,2048135740,1705550031,true +2020,9,12,1828238683,1953535377,false +2020,9,12,794388859,1030357327,true +2020,9,12,1549791469,644991115,false +2020,9,12,764667390,1940095987,true +2020,9,13,361195789,1278981316,true +2020,9,13,390211908,1869479411,false +2020,9,13,1128010698,869508455,false +2020,9,13,1402290130,1268755474,true +2020,9,13,1362839264,1521153800,false +2020,9,13,462614821,1120291817,false +2020,9,13,85465340,2134021756,true +2020,9,13,267210502,922140111,false +2020,9,13,644596681,670513124,false +2020,9,13,268407122,1795904144,true +2020,9,14,578857133,1555238008,false +2020,9,14,860856966,1748340330,false +2020,9,14,1882016386,369576175,true +2020,9,14,1525309121,319856402,false +2020,9,14,1255586986,122355616,true +2020,9,14,849623539,737186984,false +2020,9,14,2052899924,844134570,true +2020,9,14,306191208,1484565994,true +2020,9,14,2146484941,1960484809,false +2020,9,14,1250397186,818759168,false +2020,9,15,1268418599,1870865054,true +2020,9,15,459286963,2117615286,false +2020,9,15,2072638378,486720406,true +2020,9,15,1410760215,1856023559,false +2020,9,15,455859143,180946103,false +2020,9,15,1401104196,2087919167,true +2020,9,15,631089534,934343412,false +2020,9,15,55019810,206683678,true +2020,9,15,47346553,1580094944,true +2020,9,15,574049006,1213764294,true +2020,9,16,371376980,1771186387,false +2020,9,16,1947043769,111692634,false +2020,9,16,659397566,457038303,true +2020,9,16,1051402740,1983239826,false +2020,9,16,2082300025,76720166,false +2020,9,16,731375991,127369396,true +2020,9,16,799603047,1260613234,false +2020,9,16,836536447,1963543735,false +2020,9,16,1609269283,1954709534,false +2020,9,16,1534543764,572872562,false +2020,9,17,972622810,1712311723,true +2020,9,17,1721432414,2087803945,false +2020,9,17,592138589,1126407075,false +2020,9,17,377263813,453933014,false +2020,9,17,2112740771,2136703370,false +2020,9,17,736203009,872291585,true +2020,9,17,1059077942,1318025235,true +2020,9,17,1343517271,770726917,true +2020,9,17,1492684607,138822873,false +2020,9,17,295591864,524370958,false +2020,9,18,2041447723,1883758398,false +2020,9,18,2011643015,1247395642,false +2020,9,18,518039588,1701530956,true +2020,9,18,986310440,1658583199,true +2020,9,18,1612666833,1571242146,true +2020,9,18,300552229,266577196,false +2020,9,18,852646420,1549417176,false +2020,9,18,1023074062,511344784,false +2020,9,18,2012736967,184714724,true +2020,9,18,269205353,614696380,true +2020,9,19,1114055332,1282910292,false +2020,9,19,614964537,1684663956,false +2020,9,19,1892152540,1021804175,true +2020,9,19,1803836436,841050339,false +2020,9,19,204371720,1234671951,true +2020,9,19,1229438146,2071740098,false +2020,9,19,945373052,1035484944,false +2020,9,19,29689035,46333660,false +2020,9,19,537683489,1424965937,false +2020,9,19,1959125133,830704994,true +2020,9,20,467449440,1152716529,false +2020,9,20,1096348592,277807246,true +2020,9,20,664820938,1665326998,false +2020,9,20,157986053,1577931769,false +2020,9,20,194566775,2106835542,false +2020,9,20,1861749314,1317692476,true +2020,9,20,1376680030,584560102,false +2020,9,20,1876907284,74635410,true +2020,9,20,1686135567,676108982,true +2020,9,20,668358263,335229552,true +2020,9,21,926552974,166795833,true +2020,9,21,794252897,738999120,false +2020,9,21,539640905,1862929114,true +2020,9,21,757288216,617809701,true +2020,9,21,1426094433,1555811178,true +2020,9,21,1134444750,1402457650,true +2020,9,21,270707131,661256731,false +2020,9,21,451851613,950373172,false +2020,9,21,59697804,1220553337,true +2020,9,21,183477553,1408738085,true +2020,9,22,2042016103,733766360,false +2020,9,22,327713225,1109225985,false +2020,9,22,1724342517,1636396327,true +2020,9,22,392464210,2088991824,false +2020,9,22,1936913116,467953718,false +2020,9,22,2100040263,1209845606,false +2020,9,22,1745599874,660430285,true +2020,9,22,1084419877,1117096603,true +2020,9,22,1238310571,188104477,true +2020,9,22,194943878,1608227296,true +2020,9,23,1073171495,2062310524,true +2020,9,23,295269705,82617985,false +2020,9,23,892289549,744326767,true +2020,9,23,127643538,1353338105,true +2020,9,23,281725488,554194267,true +2020,9,23,111797953,1279192856,false +2020,9,23,616397523,1296362122,false +2020,9,23,733619124,1991974902,true +2020,9,23,987192011,620013629,true +2020,9,23,167468017,857996127,true +2020,9,24,1877288846,643903488,false +2020,9,24,1327419276,2126558401,false +2020,9,24,172561881,1610628482,true +2020,9,24,419258076,2056653255,false +2020,9,24,262856703,2045182357,true +2020,9,24,991551852,1082499865,true +2020,9,24,1804737782,1326859312,false +2020,9,24,2125659293,1944967684,false +2020,9,24,167428545,1939086715,true +2020,9,24,1995359223,253977592,true +2020,9,25,799236807,1141462354,true +2020,9,25,1835392548,799874325,false +2020,9,25,2140200245,2014428618,false +2020,9,25,231067898,1382883653,false +2020,9,25,1968738436,1037039760,true +2020,9,25,1999029298,266839954,false +2020,9,25,563465902,1187993666,true +2020,9,25,325209483,985478588,true +2020,9,25,1480635845,435734031,false +2020,9,25,1111622294,1742983734,true +2020,9,26,313249758,1103761732,true +2020,9,26,770599777,1054501146,true +2020,9,26,142454417,1871159692,true +2020,9,26,1288427177,383693997,false +2020,9,26,137809281,2135326222,true +2020,9,26,591547251,1063007372,false +2020,9,26,1376807845,1098173108,false +2020,9,26,1809954056,335619743,true +2020,9,26,409226568,961516087,false +2020,9,26,1203387859,1544788826,false +2020,9,27,1842383105,443137734,false +2020,9,27,1467148946,599700221,false +2020,9,27,1978377,1494668394,false +2020,9,27,1631304396,46687184,true +2020,9,27,969006777,1297557356,false +2020,9,27,1167338658,223340940,false +2020,9,27,1575328805,2018981479,false +2020,9,27,1135595835,324058222,false +2020,9,27,1404762107,73028311,true +2020,9,27,936309901,805157551,true +2020,9,28,1003592761,1407003704,false +2020,9,28,37553708,1072404817,true +2020,9,28,513514588,1918890265,false +2020,9,28,1690747486,96717177,false +2020,9,28,802635273,2140397232,true +2020,9,28,1797906869,2048153056,true +2020,9,28,971691880,423957191,true +2020,9,28,1882432208,430694387,false +2020,9,28,106380713,1093702953,true +2020,9,28,1087153431,939550395,false +2020,9,29,332933737,1462150351,false +2020,9,29,500339853,242020779,false +2020,9,29,334465261,1913303678,true +2020,9,29,1501816339,1843275744,true +2020,9,29,1485558902,1062921623,false +2020,9,29,684304550,1133271166,true +2020,9,29,2029856532,773841223,false +2020,9,29,1487342473,66982688,false +2020,9,29,1571708096,259493720,true +2020,9,29,324467283,1589655462,false +2020,9,30,1733644464,82922324,true +2020,9,30,1542252376,2022566594,true +2020,9,30,945277721,2081076154,true +2020,9,30,1639373293,1390293255,false +2020,9,30,969065433,1410332781,true +2020,9,30,1362225522,1555539062,false +2020,9,30,1282095814,1015776432,true +2020,9,30,1782499112,1870256310,true +2020,9,30,1359388819,34844744,false +2020,9,30,957261886,1779179644,true +2020,9,31,1627632519,130254485,false +2020,9,31,1928861333,45781707,false +2020,9,31,1807374716,1855519690,true +2020,9,31,680704563,40106261,true +2020,9,31,339965155,1925703560,true +2020,9,31,1439587928,1008781161,true +2020,9,31,1371178170,671097219,false +2020,9,31,687283378,1376712234,false +2020,9,31,198123449,1909323781,false +2020,9,31,250283558,1362008331,true +2020,10,1,1090033957,1027619172,false +2020,10,1,477537315,1451553084,true +2020,10,1,79849989,620036293,true +2020,10,1,5805698,376473873,false +2020,10,1,754181040,1484174275,false +2020,10,1,217198932,1527854476,true +2020,10,1,675039471,1892348375,true +2020,10,1,163947320,1853273739,false +2020,10,1,2078818590,422375314,false +2020,10,1,1893312527,1029941387,false +2020,10,2,1862057242,1813554944,true +2020,10,2,1584950554,1731175237,false +2020,10,2,1899718626,1658502107,false +2020,10,2,910287610,1976649349,false +2020,10,2,959884587,1873132880,false +2020,10,2,1836685051,1392618259,false +2020,10,2,605539926,1614143615,true +2020,10,2,811278515,1989656457,true +2020,10,2,1307343511,308491781,false +2020,10,2,1005423081,3709278,true +2020,10,3,129399073,1302596143,true +2020,10,3,1105372716,1257128091,true +2020,10,3,556678322,1376215717,false +2020,10,3,1589732527,891357424,false +2020,10,3,1087847907,132494763,false +2020,10,3,1961726532,1313846558,true +2020,10,3,1620889078,1323095638,true +2020,10,3,1249481354,1136359932,true +2020,10,3,1838237218,1326315067,true +2020,10,3,1053433511,108596348,true +2020,10,4,1542264720,1318672513,false +2020,10,4,223700266,1133333105,true +2020,10,4,305913466,1951675243,false +2020,10,4,1391446504,1397772311,false +2020,10,4,1510688561,515970216,true +2020,10,4,744246765,1560149121,true +2020,10,4,200400384,713177611,false +2020,10,4,2068719325,1200592245,false +2020,10,4,1697504019,413500292,true +2020,10,4,1451659863,237205356,true +2020,10,5,2137010679,804404326,true +2020,10,5,66932026,534845607,false +2020,10,5,384086090,548850018,false +2020,10,5,1565300693,387985042,false +2020,10,5,225073590,1702731202,false +2020,10,5,1973561201,1280838394,false +2020,10,5,590905165,1709278656,true +2020,10,5,1723671220,1652710073,true +2020,10,5,1190518489,1520870680,false +2020,10,5,1778653641,1042014690,true +2020,10,6,1837331141,1163022372,true +2020,10,6,2114724956,111915420,false +2020,10,6,304092583,1005351701,true +2020,10,6,43564733,1492267556,false +2020,10,6,487310485,678649404,false +2020,10,6,60355793,376260532,false +2020,10,6,1837085124,400152402,true +2020,10,6,1986901144,1112892226,true +2020,10,6,685083946,261099324,false +2020,10,6,1609831276,989038667,false +2020,10,7,1042152216,1765597391,false +2020,10,7,1293400142,1446299436,false +2020,10,7,198783237,410867827,false +2020,10,7,1035061973,1176137662,true +2020,10,7,1791602432,1817068982,false +2020,10,7,1818855837,1620369957,true +2020,10,7,319821958,2134200112,false +2020,10,7,1058559045,76182546,false +2020,10,7,1334303155,1084554830,false +2020,10,7,1475105892,994897651,false +2020,10,8,2010750459,1673481172,false +2020,10,8,372594171,1892861883,true +2020,10,8,913650679,1575202621,false +2020,10,8,1487313732,72411979,false +2020,10,8,559308265,373645881,true +2020,10,8,1658837244,240509608,false +2020,10,8,883216644,744144642,false +2020,10,8,720232309,60143739,true +2020,10,8,146822554,219884354,false +2020,10,8,702616079,445218194,false +2020,10,9,162474168,1084437170,true +2020,10,9,1946845675,1273437274,true +2020,10,9,1438284775,972626883,false +2020,10,9,1580177912,251882375,true +2020,10,9,2079771134,12299762,true +2020,10,9,1268554154,943566445,false +2020,10,9,38977608,822797460,false +2020,10,9,806744308,760282546,true +2020,10,9,2053521785,1840394806,true +2020,10,9,2050210662,998571355,false +2020,10,10,1228774426,1994823664,false +2020,10,10,1636198773,954654061,false +2020,10,10,1926561909,497187129,false +2020,10,10,279637598,1938847891,false +2020,10,10,233480751,123047956,false +2020,10,10,302241311,1104382089,false +2020,10,10,2056736783,705300156,true +2020,10,10,1205160570,91744828,true +2020,10,10,62240924,1866945301,true +2020,10,10,44537603,1183243229,false +2020,10,11,1497341090,493374851,true +2020,10,11,1193212836,972721502,true +2020,10,11,1911089343,361885697,false +2020,10,11,187359771,976574477,true +2020,10,11,1149609334,1142064013,true +2020,10,11,1480173156,1746537550,true +2020,10,11,1405022607,1904906554,false +2020,10,11,112354054,308153501,false +2020,10,11,1004690596,1313866131,true +2020,10,11,2130340287,320664415,false +2020,10,12,811534376,423903247,true +2020,10,12,699436549,923161910,false +2020,10,12,1391821645,1753041347,false +2020,10,12,1954216529,1540514759,false +2020,10,12,1025405590,1688343964,false +2020,10,12,482624054,278320439,true +2020,10,12,1199328764,1750667710,false +2020,10,12,748065450,1938232015,true +2020,10,12,913631458,1000690916,true +2020,10,12,575051536,1646878080,true +2020,10,13,851352920,1610607568,false +2020,10,13,440317158,501080384,true +2020,10,13,1346168923,1589062189,false +2020,10,13,277082399,1103603835,false +2020,10,13,1126340367,1589144431,true +2020,10,13,455276944,1173133066,true +2020,10,13,1313957159,610339739,false +2020,10,13,1084340736,1473714283,true +2020,10,13,1095543551,1355473143,false +2020,10,13,1258448824,249007926,false +2020,10,14,1369343498,1513486096,false +2020,10,14,1295210399,1307960313,true +2020,10,14,1360285862,1366624419,false +2020,10,14,1945375314,955080718,true +2020,10,14,1703630454,2077886787,true +2020,10,14,1253981873,438459645,true +2020,10,14,1239681228,1071335631,false +2020,10,14,407369426,1580583818,false +2020,10,14,75406035,341649951,false +2020,10,14,321272941,1734688138,false +2020,10,15,538275695,15096685,false +2020,10,15,1569741062,1690665982,true +2020,10,15,1919496976,1623247454,false +2020,10,15,1919964047,2007246652,true +2020,10,15,1774217711,1736801989,false +2020,10,15,1546590260,372412675,false +2020,10,15,804566890,801107559,false +2020,10,15,45861006,1559871686,false +2020,10,15,1502002555,974293361,true +2020,10,15,1823062389,1652252835,true +2020,10,16,1383414652,1074528868,false +2020,10,16,1845377308,573906359,false +2020,10,16,828611003,165051800,true +2020,10,16,558506832,1305792713,false +2020,10,16,306799496,1013461928,false +2020,10,16,423156448,1681022092,true +2020,10,16,1387324351,2783082,false +2020,10,16,1349600922,207731943,false +2020,10,16,1697396741,1637894976,true +2020,10,16,96615792,1591387900,false +2020,10,17,531254568,782897885,false +2020,10,17,1203515534,286317545,true +2020,10,17,1784701801,1183270360,false +2020,10,17,1830890385,453116031,false +2020,10,17,1509458837,1017226406,true +2020,10,17,1257456168,1443101275,true +2020,10,17,1707918904,1463100624,true +2020,10,17,1376946099,1011715873,false +2020,10,17,1367147601,1049879163,true +2020,10,17,2028898503,1899355350,false +2020,10,18,190745933,65189023,false +2020,10,18,1626161063,302860934,false +2020,10,18,1659985979,2090849927,false +2020,10,18,627667971,2010273456,false +2020,10,18,529348201,1313162344,true +2020,10,18,166507427,432028989,false +2020,10,18,389217269,620313134,true +2020,10,18,1986749643,706633956,false +2020,10,18,317796090,661359322,true +2020,10,18,607857784,1477624905,false +2020,10,19,1451661393,936023042,false +2020,10,19,1739706819,287587647,false +2020,10,19,129890605,286798925,false +2020,10,19,539180306,2076866798,true +2020,10,19,251473657,351909126,true +2020,10,19,1135344861,1524232653,false +2020,10,19,1457324855,1236129307,false +2020,10,19,886582065,2139336889,false +2020,10,19,1722675037,1235513634,true +2020,10,19,693597750,1634117764,true +2020,10,20,295607519,1177764668,true +2020,10,20,34257766,966836179,false +2020,10,20,1777297378,906276583,true +2020,10,20,367519796,2059779887,false +2020,10,20,1924839248,872840741,true +2020,10,20,574684241,1871931783,true +2020,10,20,2002528073,738208388,true +2020,10,20,1507584221,1334612893,false +2020,10,20,803953438,952091618,true +2020,10,20,400878873,1895639825,true +2020,10,21,528310877,1580828680,true +2020,10,21,1479789690,1984583690,true +2020,10,21,185008279,869100658,true +2020,10,21,527899437,37964969,true +2020,10,21,2024827361,507875546,true +2020,10,21,1563342188,622296013,true +2020,10,21,1885943372,666147939,true +2020,10,21,747082553,1801446608,true +2020,10,21,159513785,435206263,false +2020,10,21,1836932430,951350340,false +2020,10,22,1592541663,1776419759,false +2020,10,22,1883816300,2033269211,true +2020,10,22,1888272984,981877867,false +2020,10,22,1350652749,676196861,true +2020,10,22,1844881166,1154557398,true +2020,10,22,519904488,864890301,true +2020,10,22,824605033,285979537,false +2020,10,22,758295492,1524551206,false +2020,10,22,510378240,1380443965,true +2020,10,22,2020194954,858693658,true +2020,10,23,1813096130,111165583,true +2020,10,23,988110108,527842359,false +2020,10,23,2112281040,800545734,false +2020,10,23,907879131,107934345,true +2020,10,23,2067779717,1676254092,false +2020,10,23,1654834658,950209503,false +2020,10,23,785149016,2126647984,false +2020,10,23,603653975,1155930920,true +2020,10,23,598905240,1424766773,false +2020,10,23,1415780394,1026230060,false +2020,10,24,2108521536,1421040560,true +2020,10,24,458928861,247460198,true +2020,10,24,1626870088,1528743152,false +2020,10,24,1516611107,1363654910,true +2020,10,24,326447175,916078929,true +2020,10,24,174018934,655728909,false +2020,10,24,1188670266,1161149535,false +2020,10,24,1836469244,69216173,true +2020,10,24,2116098768,704525449,true +2020,10,24,1732668235,1999007630,false +2020,10,25,2010764178,1045236247,false +2020,10,25,1146712822,291590138,true +2020,10,25,362339504,1560454557,false +2020,10,25,226111201,354631570,true +2020,10,25,642810726,1811628450,true +2020,10,25,1478453706,1458400308,true +2020,10,25,970591798,1368000056,false +2020,10,25,1199728279,1386512062,false +2020,10,25,982759781,1471868595,false +2020,10,25,1677921854,1994150827,true +2020,10,26,22649468,650113222,true +2020,10,26,1354249570,1997695430,true +2020,10,26,1323555524,322167208,true +2020,10,26,49742851,649428570,true +2020,10,26,1282668782,681931030,true +2020,10,26,455472839,662950581,false +2020,10,26,1242169450,327652166,false +2020,10,26,878703825,1780906192,true +2020,10,26,1665790233,1191815728,false +2020,10,26,880664090,1243600251,false +2020,10,27,402160732,756612016,true +2020,10,27,1039242693,163501152,false +2020,10,27,12047541,1241798003,false +2020,10,27,1875849462,220407480,false +2020,10,27,1015817067,1251571881,true +2020,10,27,1199009275,699195274,true +2020,10,27,493968373,1389287155,false +2020,10,27,198916348,1804200135,true +2020,10,27,872883996,1857134347,true +2020,10,27,1606523496,1901376169,true +2020,10,28,1140101134,2046550802,true +2020,10,28,249579727,1229665039,false +2020,10,28,121932760,581309320,false +2020,10,28,1161366266,1059413213,false +2020,10,28,1866100742,684519773,true +2020,10,28,60977232,1900787590,false +2020,10,28,1932887893,488490656,true +2020,10,28,973468915,1015430460,true +2020,10,28,1561082815,1610610857,false +2020,10,28,1202109463,770781831,false +2020,10,29,473453380,2112892898,false +2020,10,29,7763586,676910132,true +2020,10,29,670077251,874983535,true +2020,10,29,725556275,1557172437,false +2020,10,29,578961469,1709139047,false +2020,10,29,2041751459,60397892,false +2020,10,29,1845217362,1404031844,true +2020,10,29,1483361826,613218287,true +2020,10,29,2104300775,1050354827,true +2020,10,29,604410936,660906204,false +2020,10,30,800274474,1584144221,true +2020,10,30,1089234451,1810242711,false +2020,10,30,1144653164,530529519,true +2020,10,30,945937257,776543889,true +2020,10,30,115561366,1593839202,true +2020,10,30,1168233307,1483248542,false +2020,10,30,1141559068,582014636,true +2020,10,30,2103439417,279515014,true +2020,10,30,1594148666,2100790544,true +2020,10,30,1277688885,477300978,false +2020,10,31,1219216481,237736665,false +2020,10,31,578500458,891328791,true +2020,10,31,701826589,1783624245,false +2020,10,31,332123327,1301252330,false +2020,10,31,1888474339,926453056,true +2020,10,31,1698201234,1725622215,false +2020,10,31,1096478840,1994300326,true +2020,10,31,847238449,532170704,false +2020,10,31,1884413436,967746140,true +2020,10,31,761235582,1418762860,true +2020,11,1,250642622,1383601251,true +2020,11,1,900256608,1715704752,false +2020,11,1,1198313310,1375507607,false +2020,11,1,972597960,669910426,false +2020,11,1,1532406508,1087674351,false +2020,11,1,1276691315,2049104992,true +2020,11,1,451309571,375057602,true +2020,11,1,1745270964,1989008205,true +2020,11,1,1083234835,1814054633,false +2020,11,1,366334948,1696398801,true +2020,11,2,60739414,719106517,false +2020,11,2,1259321168,1473184474,false +2020,11,2,1636415523,968196335,true +2020,11,2,244046010,1693726869,false +2020,11,2,2031660704,520836072,false +2020,11,2,2008946379,768781592,false +2020,11,2,1200827386,854251445,false +2020,11,2,1879316997,1581024432,false +2020,11,2,1193111221,248211057,false +2020,11,2,2004315413,404877514,true +2020,11,3,2028976557,941971297,false +2020,11,3,771266970,927410852,true +2020,11,3,1624853983,580191680,true +2020,11,3,1375006137,1276672848,false +2020,11,3,402370838,373497898,false +2020,11,3,860695569,1177865911,false +2020,11,3,1731396107,1070859192,true +2020,11,3,485825271,1987846543,false +2020,11,3,511760031,1995541808,false +2020,11,3,359268604,607607432,false +2020,11,4,1718876979,1489582032,true +2020,11,4,994405413,1658805534,false +2020,11,4,1048539725,14509214,true +2020,11,4,1684043369,1636921554,true +2020,11,4,18143755,1724354122,false +2020,11,4,1223025992,214892979,false +2020,11,4,797176080,1310903550,true +2020,11,4,1357665179,2083608447,false +2020,11,4,1458300656,1633989298,false +2020,11,4,52943855,1553577677,true +2020,11,5,2008581194,1530266588,false +2020,11,5,1804125956,1143047153,true +2020,11,5,693212429,74346906,false +2020,11,5,1722049871,435814818,true +2020,11,5,1082551197,1743589791,true +2020,11,5,1962001287,1471231372,true +2020,11,5,1112628688,342568340,true +2020,11,5,1526606245,1381100363,false +2020,11,5,1510659322,1688073892,false +2020,11,5,1741134060,1399573128,true +2020,11,6,116097164,291308400,false +2020,11,6,1792383176,1522110254,true +2020,11,6,2099216103,1470893469,true +2020,11,6,937681827,569752309,false +2020,11,6,1862693488,145521079,true +2020,11,6,107330573,1344282029,false +2020,11,6,327125265,1369612073,false +2020,11,6,92549054,1542612495,false +2020,11,6,1093995176,1959319400,true +2020,11,6,908796256,627646221,true +2020,11,7,786767634,676979380,false +2020,11,7,1409280809,1651383749,false +2020,11,7,1361622738,1785822706,true +2020,11,7,2046663584,1627783849,false +2020,11,7,1830611332,702474384,false +2020,11,7,1955365676,1026301060,true +2020,11,7,1438991267,737993330,true +2020,11,7,493409339,491669080,false +2020,11,7,1178104134,523118933,false +2020,11,7,1625016611,1676169545,true +2020,11,8,2073429466,697238063,true +2020,11,8,1292823050,2030486044,true +2020,11,8,1300291854,1652799094,false +2020,11,8,1852177020,2086219239,false +2020,11,8,840593254,1699376723,false +2020,11,8,800823663,1013113868,false +2020,11,8,1406128432,240338980,false +2020,11,8,624035825,1892017045,true +2020,11,8,1916951763,727468778,true +2020,11,8,700819886,714139779,true +2020,11,9,1422133678,774218856,true +2020,11,9,1261172744,848935355,true +2020,11,9,1254139643,1922358560,false +2020,11,9,2007997321,1960073867,true +2020,11,9,1311008384,719330172,true +2020,11,9,1792180424,1270704894,false +2020,11,9,1339344106,172721335,false +2020,11,9,652727582,43018372,false +2020,11,9,1676364325,1839076714,false +2020,11,9,1752839357,1189852298,false +2020,11,10,1611827730,1356217544,false +2020,11,10,240371461,1169021209,false +2020,11,10,1110002282,1753476329,false +2020,11,10,534109999,1424917408,true +2020,11,10,1388399792,35782587,true +2020,11,10,758695590,1824435301,false +2020,11,10,1919812474,420320115,true +2020,11,10,589360031,955673952,true +2020,11,10,787087486,322611849,false +2020,11,10,1800968664,1348711777,false +2020,11,11,720518439,1687337643,true +2020,11,11,158463106,1800297687,false +2020,11,11,542511515,1963866928,false +2020,11,11,972484544,537311122,false +2020,11,11,2090258040,1531805422,false +2020,11,11,1503712578,1841931084,true +2020,11,11,1909918648,1236305527,true +2020,11,11,1748517608,1194710256,true +2020,11,11,540011379,1767886647,false +2020,11,11,2114771826,313710280,true +2020,11,12,1584486787,2065285348,true +2020,11,12,1998708894,1226004505,true +2020,11,12,365218093,1969163832,true +2020,11,12,1521283920,1945512676,true +2020,11,12,1437771865,2081126139,false +2020,11,12,1874203024,1855923895,false +2020,11,12,1470612495,169126071,true +2020,11,12,91024257,922890199,true +2020,11,12,785824807,716492984,true +2020,11,12,1098293157,2002610223,false +2020,11,13,1104376939,1386585382,false +2020,11,13,1211116552,279323583,false +2020,11,13,1520680865,202914895,false +2020,11,13,1313027359,1716006027,false +2020,11,13,437083455,1181888647,false +2020,11,13,1596161159,223167024,false +2020,11,13,425286824,1046076451,true +2020,11,13,1263554799,927997866,false +2020,11,13,690954568,33180798,false +2020,11,13,1419031995,463126011,false +2020,11,14,149694709,1111446533,true +2020,11,14,186043479,3314855,false +2020,11,14,1206086219,220873400,true +2020,11,14,1217428018,333074753,false +2020,11,14,206648074,1753665967,false +2020,11,14,450571718,1113159047,false +2020,11,14,56356511,182907260,true +2020,11,14,1430999631,1010143160,true +2020,11,14,1729346394,1905552512,true +2020,11,14,487015665,387179302,true +2020,11,15,1598701823,1388283279,true +2020,11,15,1830602624,174483260,false +2020,11,15,1292985784,210385016,false +2020,11,15,692571182,1140625764,false +2020,11,15,871716495,1368806733,true +2020,11,15,1926257341,2113108633,true +2020,11,15,1028091645,24160375,false +2020,11,15,829623061,387263371,true +2020,11,15,534900525,1243189509,true +2020,11,15,881422917,1814250135,false +2020,11,16,2085991166,803347378,true +2020,11,16,1743566426,1631341828,false +2020,11,16,241978362,1866974357,true +2020,11,16,1135385237,1862213959,false +2020,11,16,1744998062,1140175479,true +2020,11,16,382109924,1181604412,false +2020,11,16,1847975976,456877638,true +2020,11,16,611432106,2100810638,false +2020,11,16,886893199,1294648866,false +2020,11,16,1736725043,1978910160,true +2020,11,17,84535301,273936106,false +2020,11,17,1563425655,264301807,true +2020,11,17,1247279454,1377280095,false +2020,11,17,795702769,1301253632,false +2020,11,17,1944588873,1003108148,true +2020,11,17,1130222249,1573472670,true +2020,11,17,1151760127,1054559650,true +2020,11,17,1291131094,1509097043,false +2020,11,17,805087132,202446424,false +2020,11,17,1508114586,1184015042,true +2020,11,18,1450275750,789292772,false +2020,11,18,490567362,148439430,false +2020,11,18,802282604,1280474596,false +2020,11,18,1560586644,1288876297,false +2020,11,18,327743304,1028517536,false +2020,11,18,642915861,1979027387,false +2020,11,18,427178221,103059165,false +2020,11,18,49549896,1325173106,false +2020,11,18,555419969,909745421,false +2020,11,18,413666385,1342571744,false +2020,11,19,839926641,343499806,true +2020,11,19,815413133,1431380055,false +2020,11,19,1947723847,1758607261,true +2020,11,19,302114803,2127129437,true +2020,11,19,337631467,2081394157,true +2020,11,19,789158237,941760186,false +2020,11,19,191921807,1343115190,false +2020,11,19,574739343,433227522,true +2020,11,19,680648638,1736997599,true +2020,11,19,1934152973,143291857,true +2020,11,20,1577999934,231146672,true +2020,11,20,20551210,1396419631,false +2020,11,20,292241610,755321326,true +2020,11,20,106556109,2051300564,true +2020,11,20,1006817810,458007823,false +2020,11,20,1886196957,881241926,false +2020,11,20,427578041,1644282328,true +2020,11,20,708004448,678733062,false +2020,11,20,1807011410,1039813378,true +2020,11,20,775575975,933328974,true +2020,11,21,1971725191,1297639867,true +2020,11,21,820249373,2004848642,true +2020,11,21,1728826867,565940252,false +2020,11,21,641049491,912393961,false +2020,11,21,1052724305,271187694,false +2020,11,21,815641382,1032033352,false +2020,11,21,1710745612,2144121938,true +2020,11,21,637484043,1241720,true +2020,11,21,1190542175,1913504802,false +2020,11,21,18616807,1613413384,true +2020,11,22,1132833148,1347343413,false +2020,11,22,61607291,1260106380,true +2020,11,22,1642799220,615998906,false +2020,11,22,1209741777,56855086,false +2020,11,22,891715290,819399280,false +2020,11,22,71907114,682507153,true +2020,11,22,1344530261,341995368,false +2020,11,22,1921176920,1870565807,false +2020,11,22,20757149,1143511173,false +2020,11,22,1531845205,502119350,true +2020,11,23,1538321560,1980927753,true +2020,11,23,1373532971,1617308434,true +2020,11,23,780960653,1326412484,true +2020,11,23,943168653,1635288096,true +2020,11,23,1499027631,2018713794,false +2020,11,23,1357301171,1231680578,true +2020,11,23,573650794,252824461,false +2020,11,23,726309715,1373727311,true +2020,11,23,1991396660,2144845915,true +2020,11,23,1233846735,1000867474,false +2020,11,24,1902185947,682392007,false +2020,11,24,1457179890,833826256,false +2020,11,24,167530615,611507164,false +2020,11,24,307040462,1857687314,true +2020,11,24,1071581081,1598510752,false +2020,11,24,1152289436,1146269221,false +2020,11,24,1770431302,629572454,false +2020,11,24,1260743085,2117383441,false +2020,11,24,1472034268,2017717894,true +2020,11,24,678379465,213581042,true +2020,11,25,1074294012,1956573780,false +2020,11,25,1522558354,1090255603,false +2020,11,25,54633990,2008686950,false +2020,11,25,1841920716,937465961,true +2020,11,25,1746659789,1787661714,true +2020,11,25,1459628937,1538229557,true +2020,11,25,1788050831,803442213,true +2020,11,25,1009703606,13064695,false +2020,11,25,1158678353,610161469,true +2020,11,25,1183027070,1807610814,false +2020,11,26,1823085105,522184028,false +2020,11,26,1750554224,4252976,true +2020,11,26,1893276495,1126233980,true +2020,11,26,667123835,658946409,false +2020,11,26,1238927956,723615575,true +2020,11,26,1709611284,236954361,true +2020,11,26,944349387,2029660831,false +2020,11,26,1952834083,1024859600,false +2020,11,26,1992085948,494063952,true +2020,11,26,926773132,1504482549,true +2020,11,27,1982342994,143180918,true +2020,11,27,1206775150,2046252378,true +2020,11,27,341569138,761437673,true +2020,11,27,398361454,865897778,true +2020,11,27,1299177605,1990054131,false +2020,11,27,1597469862,60087175,true +2020,11,27,640549360,146668776,true +2020,11,27,1793646358,1088132627,true +2020,11,27,1498249188,2060827139,true +2020,11,27,627812003,1197964156,true +2020,11,28,217989776,1993643574,false +2020,11,28,300021510,1401656961,false +2020,11,28,544874717,1191971497,true +2020,11,28,1654336835,1812509386,false +2020,11,28,907114807,55880730,false +2020,11,28,513553530,1657110094,false +2020,11,28,1471350549,287999837,false +2020,11,28,723698477,786258564,true +2020,11,28,88067902,1473892530,false +2020,11,28,1626750288,1112180457,true +2020,11,29,1450413302,733452197,true +2020,11,29,1286905031,364495488,true +2020,11,29,1188362401,651322034,false +2020,11,29,742028567,362632265,true +2020,11,29,269288570,688340209,true +2020,11,29,1771192436,1578674925,false +2020,11,29,2002519660,2145725793,true +2020,11,29,462254965,372310547,true +2020,11,29,334941751,5051067,true +2020,11,29,460302867,671270934,false +2020,11,30,1144679778,679593938,false +2020,11,30,1868393407,1052666126,false +2020,11,30,553519786,1816466400,true +2020,11,30,283235765,1007991925,false +2020,11,30,879234287,1375965023,true +2020,11,30,1040433619,782117066,true +2020,11,30,2048149960,1979457849,true +2020,11,30,2036338077,544060668,false +2020,11,30,484983376,1292392811,true +2020,11,30,1002784825,327172492,true +2020,11,31,388031683,78677373,true +2020,11,31,1879086974,1241060255,true +2020,11,31,489313281,1318139992,true +2020,11,31,1802822522,1581271676,false +2020,11,31,1521020442,1259205211,false +2020,11,31,997629168,1421464614,true +2020,11,31,1684390247,41217991,false +2020,11,31,1852804800,1160220690,true +2020,11,31,1822286820,1802482388,true +2020,11,31,1437625140,952014642,false \ No newline at end of file diff --git a/athena-example/src/main/java/com/amazonaws/connectors/athena/example/ExampleCompositeHandler.java b/athena-example/src/main/java/com/amazonaws/connectors/athena/example/ExampleCompositeHandler.java new file mode 100644 index 0000000000..a9a6cb199a --- /dev/null +++ b/athena-example/src/main/java/com/amazonaws/connectors/athena/example/ExampleCompositeHandler.java @@ -0,0 +1,35 @@ +/*- + * #%L + * athena-example + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.example; + +import com.amazonaws.athena.connector.lambda.handlers.CompositeHandler; + +/** + * Boilerplate composite handler that allows us to use a single Lambda function for both + * Metadata and Data. In this case we just compose ExampleMetadataHandler and ExampleRecordHandler. + */ +public class ExampleCompositeHandler + extends CompositeHandler +{ + public ExampleCompositeHandler() + { + super(new ExampleMetadataHandler(), new ExampleRecordHandler()); + } +} diff --git a/athena-example/src/main/java/com/amazonaws/connectors/athena/example/ExampleMetadataHandler.java b/athena-example/src/main/java/com/amazonaws/connectors/athena/example/ExampleMetadataHandler.java new file mode 100644 index 0000000000..e2a1462ea5 --- /dev/null +++ b/athena-example/src/main/java/com/amazonaws/connectors/athena/example/ExampleMetadataHandler.java @@ -0,0 +1,283 @@ +/*- + * #%L + * athena-example + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.example; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockWriter; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.handlers.MetadataHandler; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesResponse; +import org.apache.arrow.vector.complex.reader.FieldReader; +//DO NOT REMOVE - this will not be _unused_ when customers go through the tutorial and uncomment +//the TODOs +import org.apache.arrow.vector.types.Types; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * This class is part of an tutorial that will walk you through how to build a connector for your + * custom data source. The README for this module (athena-example) will guide you through preparing + * your development environment, modifying this example Metadatahandler, building, deploying, and then + * using your new source in an Athena query. + *

+ * More specifically, this class is responsible for providing Athena with metadata about the schemas (aka databases), + * tables, and table partitions that your source contains. Lastly, this class tells Athena how to split up reads against + * this source. This gives you control over the level of performance and parallelism your source can support. + *

+ * For more examples, please see the other connectors in this repository (e.g. athena-cloudwatch, athena-docdb, etc...) + */ +public class ExampleMetadataHandler + extends MetadataHandler +{ + private static final Logger logger = LoggerFactory.getLogger(ExampleMetadataHandler.class); + + /** + * used to aid in debugging. Athena will use this name in conjunction with your catalog id + * to correlate relevant query errors. + */ + private static final String SOURCE_TYPE = "example"; + + public ExampleMetadataHandler() + { + super(SOURCE_TYPE); + } + + /** + * Used to get the list of schemas (aka databases) that this source contains. + * + * @param allocator Tool for creating and managing Apache Arrow Blocks. + * @param request Provides details on who made the request and which Athena catalog they are querying. + * @return A ListSchemasResponse which primarily contains a Set of schema names and a catalog name + * corresponding the Athena catalog that was queried. + */ + @Override + public ListSchemasResponse doListSchemaNames(BlockAllocator allocator, ListSchemasRequest request) + { + logger.info("doListSchemaNames: enter - " + request); + + Set schemas = new HashSet<>(); + + /** + * TODO: Add schemas, example below + * + schemas.add("schema1"); + * + */ + + return new ListSchemasResponse(request.getCatalogName(), schemas); + } + + /** + * Used to get the list of tables that this source contains. + * + * @param allocator Tool for creating and managing Apache Arrow Blocks. + * @param request Provides details on who made the request and which Athena catalog and database they are querying. + * @return A ListTablesResponse which primarily contains a List enumerating the tables in this + * catalog, database tuple. It also contains the catalog name corresponding the Athena catalog that was queried. + */ + @Override + public ListTablesResponse doListTables(BlockAllocator allocator, ListTablesRequest request) + { + logger.info("doListTables: enter - " + request); + + List tables = new ArrayList<>(); + + /** + * TODO: Add tables for the requested schema, example below + * + tables.add(new TableName(request.getSchemaName(), "table1")); + * + */ + + return new ListTablesResponse(request.getCatalogName(), tables); + } + + /** + * Used to get definition (field names, types, descriptions, etc...) of a Table. + * + * @param allocator Tool for creating and managing Apache Arrow Blocks. + * @param request Provides details on who made the request and which Athena catalog, database, and table they are querying. + * @return A GetTableResponse which primarily contains: + * 1. An Apache Arrow Schema object describing the table's columns, types, and descriptions. + * 2. A Set of partition column names (or empty if the table isn't partitioned). + * 3. A TableName object confirming the schema and table name the response is for. + * 4. A catalog name corresponding the Athena catalog that was queried. + */ + @Override + public GetTableResponse doGetTable(BlockAllocator allocator, GetTableRequest request) + { + logger.info("doGetTable: enter - " + request); + + Set partitionColNames = new HashSet<>(); + + /** + * TODO: Add partitions columns, example below. + * + partitionColNames.add("year"); + partitionColNames.add("month"); + partitionColNames.add("day"); + * + */ + + SchemaBuilder tableSchemaBuilder = SchemaBuilder.newBuilder(); + + /** + * TODO: Generate a schema for the requested table. + * + tableSchemaBuilder.addIntField("year") + .addIntField("month") + .addIntField("day") + .addStringField("account_id") + .addStructField("transaction") + .addChildField("transaction", "id", Types.MinorType.INT.getType()) + .addChildField("transaction", "completed", Types.MinorType.BIT.getType()) + //Metadata who's name matches a column name + //is interpreted as the description of that + //column when you run "show tables" queries. + .addMetadata("year", "The year that the payment took place in.") + .addMetadata("month", "The month that the payment took place in.") + .addMetadata("day", "The day that the payment took place in.") + .addMetadata("account_id", "The account_id used for this payment.") + .addMetadata("transaction", "The payment transaction details.") + //This metadata field is for our own use, Athena will ignore and pass along fields it doesn't expect. + //we will use this later when we implement doGetTableLayout(...) + .addMetadata("partitionCols", "year,month,day"); + * + */ + + return new GetTableResponse(request.getCatalogName(), + request.getTableName(), + tableSchemaBuilder.build(), + partitionColNames); + } + + /** + * Used to get the partitions that must be read from the request table in order to satisfy the requested predicate. + * + * @param blockWriter Used to write rows (partitions) into the Apache Arrow response. + * @param request Provides details of the catalog, database, and table being queried as well as any filter predicate. + * @param queryStatusChecker A QueryStatusChecker that you can use to stop doing work for a query that has already terminated + * @note Partitions are partially opaque to Amazon Athena in that it only understands your partition columns and + * how to filter out partitions that do not meet the query's constraints. Any additional columns you add to the + * partition data are ignored by Athena but passed on to calls on GetSplits. + */ + @Override + public void getPartitions(BlockWriter blockWriter, GetTableLayoutRequest request, QueryStatusChecker queryStatusChecker) + throws Exception + { + for (int year = 2000; year < 2018; year++) { + for (int month = 1; month < 12; month++) { + for (int day = 1; day < 31; day++) { + + final int yearVal = year; + final int monthVal = month; + final int dayVal = day; + /** + * TODO: If the partition represented by this year,month,day offer the values to the block + * and check if they all passed constraints. The Block has been configured to automatically + * apply our partition pruning constraints. + * + blockWriter.writeRows((Block block, int row) -> { + boolean matched = true; + matched &= block.setValue("year", row, yearVal); + matched &= block.setValue("month", row, monthVal); + matched &= block.setValue("day", row, dayVal); + //If all fields matches then we wrote 1 row during this call so we return 1 + return matched ? 1 : 0; + }); + * + */ + } + } + } + } + + /** + * Used to split-up the reads required to scan the requested batch of partition(s). + * + * @param allocator Tool for creating and managing Apache Arrow Blocks. + * @param request Provides details of the catalog, database, table, andpartition(s) being queried as well as + * any filter predicate. + * @return A GetSplitsResponse which primarily contains: + * 1. A Set which represent read operations Amazon Athena must perform by calling your read function. + * 2. (Optional) A continuation token which allows you to paginate the generation of splits for large queries. + * @note A Split is a mostly opaque object to Amazon Athena. Amazon Athena will use the optional SpillLocation and + * optional EncryptionKey for pipelined reads but all properties you set on the Split are passed to your read + * function to help you perform the read. + */ + @Override + public GetSplitsResponse doGetSplits(BlockAllocator allocator, GetSplitsRequest request) + { + logger.info("doGetSplits: enter - " + request); + + String catalogName = request.getCatalogName(); + Set splits = new HashSet<>(); + + Block partitions = request.getPartitions(); + + FieldReader day = partitions.getFieldReader("day"); + FieldReader month = partitions.getFieldReader("month"); + FieldReader year = partitions.getFieldReader("year"); + for (int i = 0; i < partitions.getRowCount(); i++) { + //Set the readers to the partition row we area on + year.setPosition(i); + month.setPosition(i); + day.setPosition(i); + + /** + * TODO: For each partition in the request, create 1 or more splits. Splits + * are parallelizable units of work. Each represents a part of your table + * that needs to be read for the query. Splits are opaque to Athena aside from the + * spill location and encryption key. All properties added to a split are solely + * for your use when Athena calls your readWithContraints(...) function to perform + * the read. In this example we just need to know the partition details (year, month, day). + * + Split split = Split.newBuilder(makeSpillLocation(request), makeEncryptionKey()) + .add("year", String.valueOf(year.readInteger())) + .add("month", String.valueOf(month.readInteger())) + .add("day", String.valueOf(day.readInteger())) + .build(); + + splits.add(split); + * + */ + } + + logger.info("doGetSplits: exit - " + splits.size()); + return new GetSplitsResponse(catalogName, splits); + } +} diff --git a/athena-example/src/main/java/com/amazonaws/connectors/athena/example/ExampleRecordHandler.java b/athena-example/src/main/java/com/amazonaws/connectors/athena/example/ExampleRecordHandler.java new file mode 100644 index 0000000000..605350612b --- /dev/null +++ b/athena-example/src/main/java/com/amazonaws/connectors/athena/example/ExampleRecordHandler.java @@ -0,0 +1,191 @@ +/*- + * #%L + * athena-example + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.example; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockSpiller; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.handlers.RecordHandler; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.services.athena.AmazonAthenaClientBuilder; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import com.amazonaws.services.s3.model.S3Object; +import com.amazonaws.services.secretsmanager.AWSSecretsManagerClientBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +import static java.lang.String.format; + +/** + * This class is part of an tutorial that will walk you through how to build a connector for your + * custom data source. The README for this module (athena-example) will guide you through preparing + * your development environment, modifying this example RecordHandler, building, deploying, and then + * using your new source in an Athena query. + *

+ * More specifically, this class is responsible for providing Athena with actual rows level data from your source. Athena + * will call readWithConstraint(...) on this class for each 'Split' you generated in ExampleMetadataHandler. + *

+ * For more examples, please see the other connectors in this repository (e.g. athena-cloudwatch, athena-docdb, etc...) + */ +public class ExampleRecordHandler + extends RecordHandler +{ + private static final Logger logger = LoggerFactory.getLogger(ExampleRecordHandler.class); + + /** + * used to aid in debugging. Athena will use this name in conjunction with your catalog id + * to correlate relevant query errors. + */ + private static final String SOURCE_TYPE = "example"; + + private AmazonS3 amazonS3; + + public ExampleRecordHandler() + { + super(AmazonS3ClientBuilder.defaultClient(), AWSSecretsManagerClientBuilder.defaultClient(), AmazonAthenaClientBuilder.defaultClient(), SOURCE_TYPE); + this.amazonS3 = AmazonS3ClientBuilder.standard().build(); + } + + /** + * Used to read the row data associated with the provided Split. + * + * @param spiller A BlockSpiller that should be used to write the row data associated with this Split. + * The BlockSpiller automatically handles chunking the response, encrypting, and spilling to S3. + * @param recordsRequest Details of the read request, including: + * 1. The Split + * 2. The Catalog, Database, and Table the read request is for. + * 3. The filtering predicate (if any) + * 4. The columns required for projection. + * @param queryStatusChecker A QueryStatusChecker that you can use to stop doing work for a query that has already terminated + * @throws IOException + * @note Avoid writing >10 rows per-call to BlockSpiller.writeRow(...) because this will limit the BlockSpiller's + * ability to control Block size. The resulting increase in Block size may cause failures and reduced performance. + */ + @Override + protected void readWithConstraint(BlockSpiller spiller, ReadRecordsRequest recordsRequest, QueryStatusChecker queryStatusChecker) + throws IOException + { + logger.info("readWithConstraint: enter - " + recordsRequest.getSplit()); + + Split split = recordsRequest.getSplit(); + int splitYear = 0; + int splitMonth = 0; + int splitDay = 0; + + /** + * TODO: Extract information about what we need to read from the split. If you are following the tutorial + * this is basically the partition column values for year, month, day. + * + splitYear = split.getPropertyAsInt("year"); + splitMonth = split.getPropertyAsInt("month"); + splitDay = split.getPropertyAsInt("day"); + * + */ + + String dataBucket = null; + /** + * TODO: Get the data bucket from the env variable set by athena-example.yaml + * + dataBucket = System.getenv("data_bucket"); + * + */ + + String dataKey = format("%s/%s/%s/sample_data.csv", splitYear, splitMonth, splitDay); + + BufferedReader s3Reader = openS3File(dataBucket, dataKey); + if (s3Reader == null) { + //There is no data to read for this split. + return; + } + + //We read the transaction data line by line from our S3 object. + String line; + while ((line = s3Reader.readLine()) != null) { + logger.info("readWithConstraint: processing line " + line); + + //The sample_data.csv file is structured as year,month,day,account_id,transaction.id,transaction.complete + String[] lineParts = line.split(","); + + //We use the provided BlockSpiller to write our row data into the response. This utility is provided by + //the Amazon Athena Query Federation SDK and automatically handles breaking the data into reasonably sized + //chunks, encrypting it, and spilling to S3 if we've enabled these features. + spiller.writeRows((Block block, int rowNum) -> { + boolean rowMatched = true; + + int year = Integer.parseInt(lineParts[0]); + int month = Integer.parseInt(lineParts[1]); + int day = Integer.parseInt(lineParts[2]); + String accountId = lineParts[3]; + int transactionId = Integer.parseInt(lineParts[4]); + boolean transactionComplete = Boolean.parseBoolean(lineParts[5]); + + /** + * TODO: Write the data using the supplied Block and check if the writes passed all constraints + * before retuning how many rows we wrote. + * + rowMatched &= block.offerValue("year", rowNum, year); + rowMatched &= block.offerValue("month", rowNum, month); + rowMatched &= block.offerValue("day", rowNum, day); + + //For complex types like List and Struct, we can build a Map to conveniently set nested values + Map eventMap = new HashMap<>(); + eventMap.put("id", transactionId); + eventMap.put("completed", transactionComplete); + + rowMatched &= block.offerComplexValue("transaction", rowNum, FieldResolver.DEFAULT, eventMap); + * + */ + + /** + * TODO: The account_id field is a sensitive field, so we'd like to mask it to the last 4 before + * returning it to Athena. Note that this will mean you can only filter (where/having) + * on the masked value from Athena. + * + String maskedAcctId = accountId.length() > 4 ? accountId.substring(accountId.length() - 4) : accountId; + rowMatched &= block.offerValue("account_id", rowNum, maskedAcctId); + * + */ + + //We return the number of rows written for this invocation. In our case 1 or 0. + return rowMatched ? 1 : 0; + }); + } + } + + /** + * Helper function for checking the existence of and opening S3 Objects for read. + */ + private BufferedReader openS3File(String bucket, String key) + { + logger.info("openS3File: opening file " + bucket + ":" + key); + if (amazonS3.doesObjectExist(bucket, key)) { + S3Object obj = amazonS3.getObject(bucket, key); + logger.info("openS3File: opened file " + bucket + ":" + key); + return new BufferedReader(new InputStreamReader(obj.getObjectContent())); + } + return null; + } +} diff --git a/athena-federation-sdk-tools/LICENSE.txt b/athena-federation-sdk-tools/LICENSE.txt new file mode 100644 index 0000000000..418de4c108 --- /dev/null +++ b/athena-federation-sdk-tools/LICENSE.txt @@ -0,0 +1,174 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. \ No newline at end of file diff --git a/athena-federation-sdk-tools/README.md b/athena-federation-sdk-tools/README.md new file mode 100644 index 0000000000..5f775a8aa6 --- /dev/null +++ b/athena-federation-sdk-tools/README.md @@ -0,0 +1,9 @@ +# Amazon Athena Query Federation SDK Tools + +This module contains a collection of tools that are helpful in developing and testing Athena Query Federation components such as connectors. A detailed list +of the tools that can be found in this module can be found below. + +* **Connector Validator** - A runnable class which emulates the calls that Athena will make to your Lambda function as part of executing a + select * from .

where . The goal of this tool is to help your troubleshoot connectors by giving you visibility of what 'Athena' would + see. You can run this tool by using the helper script in the tools directory + ../tools/validate_connector.sh --lambda-func lambda_func [--record-func record_func] [--catalog catalog] [--schema schema [--table table [--constraints constraints]]] [--planning-only] [--help] \ No newline at end of file diff --git a/athena-federation-sdk-tools/pom.xml b/athena-federation-sdk-tools/pom.xml new file mode 100644 index 0000000000..6e7572af53 --- /dev/null +++ b/athena-federation-sdk-tools/pom.xml @@ -0,0 +1,92 @@ + + + + aws-athena-query-federation + com.amazonaws + 1.0 + + 4.0.0 + + athena-federation-sdk-tools + jar + Amazon Athena Query Federation SDK Tools + + + + com.amazonaws + aws-athena-federation-sdk + ${aws-athena-federation-sdk.version} + + + log4j + log4j + 1.2.17 + + + + commons-cli + commons-cli + 1.4 + + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + 3.1.0 + + checkstyle.xml + UTF-8 + true + false + false + + + + validate + validate + + check + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.1.1 + + true + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + withdep + package + + shade + + + + withdep + + + + + + + \ No newline at end of file diff --git a/athena-federation-sdk-tools/src/main/java/com/amazonaws/athena/connector/validation/ConnectorValidator.java b/athena-federation-sdk-tools/src/main/java/com/amazonaws/athena/connector/validation/ConnectorValidator.java new file mode 100644 index 0000000000..91a0966446 --- /dev/null +++ b/athena-federation-sdk-tools/src/main/java/com/amazonaws/athena/connector/validation/ConnectorValidator.java @@ -0,0 +1,528 @@ +/*- + * #%L + * Amazon Athena Query Federation SDK Tools + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connector.validation; + +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesResponse; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsResponse; +import com.amazonaws.athena.connector.lambda.security.FederatedIdentity; +import com.google.common.collect.Sets; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.arrow.vector.types.pojo.Schema; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; +import java.util.Random; +import java.util.Set; +import java.util.stream.Collectors; + +import static com.amazonaws.athena.connector.lambda.data.BlockUtils.rowToString; +import static com.amazonaws.athena.connector.validation.ConstraintParser.parseConstraints; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; +import static java.util.Objects.requireNonNull; + +/** + * This class should be used to validate deployed Lambda functions that use Athena's Federation SDK. + * It simulates the basic query patterns that Athena will send to Lambda throughout its usage so that + * broader and sometimes more subtle logical issues can be discovered before being used through Athena. + *

+ * You can run this tool using the following command: + * mvn exec:java -Dexec.mainClass=com.amazonaws.athena.connector.validation.ConnectorValidator -Dexec.args="[args]" + *

+ * This tool can also be run using the validate_connector.sh script in the tools directory under the package root: + * tools/validate_connector.sh [args] + */ +public class ConnectorValidator +{ + private static final Logger log = LoggerFactory.getLogger(ConnectorValidator.class); + + private static final Random RAND = new Random(); + + static final BlockAllocator BLOCK_ALLOCATOR = new BlockAllocatorImpl(); + + private ConnectorValidator() + { + // Intentionally left blank. + } + + /** + * The main method of this class allows the following argument pattern: + * --lambda-func lambda_func [--record-func record_func] [--catalog catalog] + * [--schema schema [--table table [--constraints constraints]]] [--planning-only] [--help] + *

+ * Run with the -h or --help options to see full argument descriptions, or see {@link TestConfig} below. + */ + public static void main(String[] args) + { + try { + TestConfig testConfig = TestConfig.fromArgs(args); + + /* + * SHOW DATABASES + */ + logTestQuery("SHOW DATABASES"); + Collection schemas = showDatabases(testConfig); + + // SHOW TABLES + final String db = testConfig.getSchemaId().isPresent() + ? testConfig.getSchemaId().get() + : getRandomElement(schemas); + log.info("Using database {}", db); + logTestQuery("SHOW TABLES IN " + db); + Collection tables = showTables(testConfig, db); + + /* + * DESCRIBE TABLE + */ + final TableName table = testConfig.getTableId().isPresent() + ? new TableName(db, testConfig.getTableId().get()) + : getRandomElement(tables); + log.info("Using table {}", toQualifiedTableName(table)); + logTestQuery("DESCRIBE " + toQualifiedTableName(table)); + GetTableResponse tableResponse = describeTable(testConfig, table); + final Schema schema = tableResponse.getSchema(); + final Set partitionColumns = tableResponse.getPartitionColumns(); + + /* + * SELECT + */ + logTestQuery("SELECT * FROM " + toQualifiedTableName(table)); + GetTableLayoutResponse tableLayout = getTableLayout(testConfig, table, schema, partitionColumns); + + GetSplitsResponse splitsResponse = getSplits(testConfig, + table, + schema, + tableLayout, + partitionColumns, + null); + + if (!testConfig.isPlanningOnly()) { + readRecords(testConfig, table, schema, splitsResponse.getSplits()); + } + else { + log.info("Skipping record reading because the arguments indicated that only the planning should be validated."); + } + + if (splitsResponse.getContinuationToken() == null) { + logSuccess(); + return; + } + + log.info("GetSplits included a continuation token: " + splitsResponse.getContinuationToken()); + log.info("Testing next batch of splits."); + + splitsResponse = getSplits(testConfig, + table, + schema, + tableLayout, + partitionColumns, + splitsResponse.getContinuationToken()); + + if (!testConfig.isPlanningOnly()) { + readRecords(testConfig, table, schema, splitsResponse.getSplits()); + } + else { + log.info("Skipping record reading because the arguments indicated that only the planning should be validated."); + } + + logSuccess(); + } + catch (Exception ex) { + logFailure(ex); + System.exit(1); + } + + System.exit(0); + } + + private static Collection showDatabases(TestConfig testConfig) + { + ListSchemasResponse schemasResponse = LambdaMetadataProvider.listSchemas(testConfig.getCatalogId(), + testConfig.getMetadataFunction(), + testConfig.getIdentity()); + final Collection schemas = schemasResponse.getSchemas(); + log.info("Found databases: " + schemas); + requireNonNull(schemas, "Returned collection of schemas was null!"); + checkState(!schemas.isEmpty(), "No schemas were returned!"); + List notLower = schemas.stream().filter(s -> !s.equals(s.toLowerCase())).collect(Collectors.toList()); + checkState(notLower.isEmpty(), + "All returned schemas must be lowercase! Found these non-lowercase schemas: " + notLower); + return schemas; + } + + private static Collection showTables(TestConfig testConfig, String db) + { + ListTablesResponse tablesResponse = LambdaMetadataProvider.listTables(testConfig.getCatalogId(), + db, + testConfig.getMetadataFunction(), + testConfig.getIdentity()); + final Collection tables = tablesResponse.getTables(); + log.info("Found tables: " + tables.stream() + .map(t -> toQualifiedTableName(t)) + .collect(Collectors.toList())); + requireNonNull(tables, "Returned collection of tables was null!"); + checkState(!tables.isEmpty(), "No tables were returned!"); + List notLower = tables.stream().filter(t -> !t.equals(new TableName(t.getSchemaName().toLowerCase(), + t.getTableName().toLowerCase()))).limit(5) + .map(t -> toQualifiedTableName(t)) + .collect(Collectors.toList()); + checkState(notLower.isEmpty(), + "All returned tables must be lowercase! Found these non-lowercase tables: " + notLower); + return tables; + } + + private static GetTableResponse describeTable(TestConfig testConfig, TableName table) + { + GetTableResponse tableResponse = LambdaMetadataProvider.getTable(testConfig.getCatalogId(), + table, + testConfig.getMetadataFunction(), + testConfig.getIdentity()); + TableName returnedTableName = tableResponse.getTableName(); + checkState(table.equals(returnedTableName), "Returned table name did not match the requested table name!" + + " Expected " + toQualifiedTableName(table) + + " but found " + toQualifiedTableName(returnedTableName)); + List notLower = tableResponse.getSchema().getFields() + .stream() + .map(Field::getName) + .filter(f -> !f.equals(f.toLowerCase())) + .collect(Collectors.toList()); + checkState(notLower.isEmpty(), + "All returned columns must be lowercase! Found these non-lowercase columns: " + notLower); + checkState(tableResponse.getSchema().getFields() + .stream().map(Field::getName) + .anyMatch(f -> !tableResponse.getPartitionColumns().contains(f)), + "Table must have at least one non-partition column!"); + Set fields = tableResponse.getSchema().getFields().stream().map(Field::getName).collect(Collectors.toSet()); + Sets.SetView difference = Sets.difference(tableResponse.getPartitionColumns(), fields); + checkState(difference.isEmpty(), "Table column list must include all partition columns! " + + "Found these partition columns which are not in the table's fields: " + + difference); + return tableResponse; + } + + private static GetTableLayoutResponse getTableLayout(TestConfig testConfig, + TableName table, + Schema schema, + Set partitionColumns) + { + Constraints constraints = parseConstraints(schema, testConfig.getConstraints()); + GetTableLayoutResponse tableLayout = LambdaMetadataProvider.getTableLayout(testConfig.getCatalogId(), + table, + constraints, + schema, + partitionColumns, + testConfig.getMetadataFunction(), + testConfig.getIdentity()); + log.info("Found " + tableLayout.getPartitions().getRowCount() + " partitions."); + checkState(tableLayout.getPartitions().getRowCount() > 0, + "Table " + toQualifiedTableName(table) + + " did not return any partitions. This can happen if the table" + + " is empty but could also indicate an issue." + + " Please populate the table or specify a different table."); + return tableLayout; + } + + private static GetSplitsResponse getSplits(TestConfig testConfig, + TableName table, + Schema schema, + GetTableLayoutResponse tableLayout, + Set partitionColumns, + String continuationToken) + { + Constraints constraints = parseConstraints(schema, testConfig.getConstraints()); + GetSplitsResponse splitsResponse = LambdaMetadataProvider.getSplits(testConfig.getCatalogId(), + table, + constraints, + tableLayout.getPartitions(), + new ArrayList<>(partitionColumns), + continuationToken, + testConfig.getMetadataFunction(), + testConfig.getIdentity()); + log.info("Found " + splitsResponse.getSplits().size() + " splits in batch."); + if (continuationToken == null) { + checkState(!splitsResponse.getSplits().isEmpty(), + "Table " + toQualifiedTableName(table) + + " did not return any splits. This can happen if the table" + + " is empty but could also indicate an issue." + + " Please populate the table or specify a different table."); + } + else { + checkState(!splitsResponse.getSplits().isEmpty(), + "Table " + toQualifiedTableName(table) + + " did not return any splits in the second batch despite returning" + + " a continuation token with the first batch."); + } + return splitsResponse; + } + + private static ReadRecordsResponse readRecords(TestConfig testConfig, + TableName table, + Schema schema, + Collection splits) + { + Constraints constraints = parseConstraints(schema, testConfig.getConstraints()); + Split split = getRandomElement(splits); + log.info("Executing randomly selected split with properties: {}", split.getProperties()); + ReadRecordsResponse records = LambdaRecordProvider.readRecords(testConfig.getCatalogId(), + table, + constraints, + schema, + split, + testConfig.getRecordFunction(), + testConfig.getIdentity()); + log.info("Received " + records.getRecordCount() + " records."); + checkState(records.getRecordCount() > 0, + "Table " + toQualifiedTableName(table) + + " did not return any rows in the tested split, even though an empty constraint was used." + + " This can happen if the table is empty but could also indicate an issue." + + " Please populate the table or specify a different table."); + log.info("Discovered columns: " + + records.getSchema().getFields() + .stream() + .map(f -> f.getName() + ":" + f.getType().getTypeID()) + .collect(Collectors.toList())); + + if (records.getRecordCount() == 0) { + return records; + } + + log.info("First row of split: " + rowToString(records.getRecords(), 0)); + + return records; + } + + private static T getRandomElement(Collection elements) + { + int i = RAND.nextInt(elements.size()); + Iterator iter = elements.iterator(); + T elem; + do { + elem = iter.next(); + i--; + } while (i >= 0); + return elem; + } + + private static void logTestQuery(String query) + { + log.info("=================================================="); + log.info("Testing " + query); + log.info("=================================================="); + } + + private static void logSuccess() + { + log.info("=================================================="); + log.info("Successfully Passed Validation!"); + log.info("=================================================="); + } + + private static void logFailure(Exception ex) + { + log.error("=================================================="); + log.error("Error Encountered During Validation!", ex); + log.error("=================================================="); + } + + private static String toQualifiedTableName(TableName name) + { + return name.getSchemaName() + "." + name.getTableName(); + } + + private static class TestConfig + { + private static final String LAMBDA_METADATA_FUNCTION_ARG = "lambda-func"; + private static final String LAMBDA_RECORD_FUNCTION_ARG = "record-func"; + private static final String CATALOG_ID_ARG = "catalog"; + private static final String SCHEMA_ID_ARG = "schema"; + private static final String TABLE_ID_ARG = "table"; + private static final String CONSTRAINTS_ARG = "constraints"; + private static final String PLANNING_ONLY_ARG = "planning-only"; + private static final String HELP_ARG = "help"; + + private final FederatedIdentity identity; + private final String metadataFunction; + private final String recordFunction; + private final String catalogId; + private final Optional schemaId; + private final Optional tableId; + private final Optional constraints; + private final boolean planningOnly; + + private TestConfig(String metadataFunction, + String recordFunction, + String catalogId, + Optional schemaId, + Optional tableId, + Optional constraints, + boolean planningOnly) + { + this.metadataFunction = metadataFunction; + this.recordFunction = recordFunction; + this.catalogId = catalogId; + this.schemaId = schemaId; + this.tableId = tableId; + this.constraints = constraints; + this.planningOnly = planningOnly; + this.identity = new FederatedIdentity("VALIDATION_ACCESS_KEY", + "VALIDATION_PRINCIPAL", + "VALIDATION_ACCOUNT"); + } + + public FederatedIdentity getIdentity() + { + return identity; + } + + String getMetadataFunction() + { + return metadataFunction; + } + + String getRecordFunction() + { + return recordFunction; + } + + String getCatalogId() + { + return catalogId; + } + + Optional getSchemaId() + { + return schemaId; + } + + Optional getTableId() + { + return tableId; + } + + Optional getConstraints() + { + return constraints; + } + + boolean isPlanningOnly() + { + return planningOnly; + } + + static TestConfig fromArgs(String[] args) throws ParseException + { + log.info("Received arguments: {}", args); + + requireNonNull(args); + + Options options = new Options(); + options.addOption("f", LAMBDA_METADATA_FUNCTION_ARG, true, + "The name of the Lambda function to be validated. " + + "Uses your configured default AWS region."); + options.addOption("r", LAMBDA_RECORD_FUNCTION_ARG, true, + "The name of the Lambda function to be used to read data records. " + + "If not provided, this defaults to the value provided for lambda-func. " + + "Uses your configured default AWS region."); + options.addOption("c", CATALOG_ID_ARG, true, + "The catalog name to pass to the Lambda function to be validated."); + options.addOption("s", SCHEMA_ID_ARG, true, + "The schema name to be used when validating the Lambda function. " + + "If not provided, a random existing schema will be chosen."); + options.addOption("t", TABLE_ID_ARG, true, + "The table name to be used when validating the Lambda function. " + + "If not provided, a random existing table will be chosen."); + options.addOption("c", CONSTRAINTS_ARG, true, + "A comma-separated list of field/value pair constraints to be applied " + + "when reading metadata and records from the Lambda function to be validated"); + options.addOption("p", PLANNING_ONLY_ARG, false, + "If this option is set, then the validator will not attempt to read" + + " any records after calling GetSplits."); + options.addOption("h", HELP_ARG, false, "Prints usage information."); + DefaultParser argParser = new DefaultParser(); + CommandLine parsedArgs = argParser.parse(options, args); + + if (parsedArgs.hasOption(HELP_ARG)) { + new HelpFormatter().printHelp(150, "./validate_connector.sh --" + LAMBDA_METADATA_FUNCTION_ARG + + " lambda_func [--" + LAMBDA_RECORD_FUNCTION_ARG + + " record_func] [--" + CATALOG_ID_ARG + + " catalog] [--" + SCHEMA_ID_ARG + + " schema [--" + TABLE_ID_ARG + + " table [--" + CONSTRAINTS_ARG + + " constraints]]] [--" + PLANNING_ONLY_ARG + "] [--" + HELP_ARG + "]", + null, + options, + null); + System.exit(0); + } + + checkArgument(parsedArgs.hasOption(LAMBDA_METADATA_FUNCTION_ARG), + "Lambda function must be provided via the --lambda-func or -l args!"); + String metadataFunction = parsedArgs.getOptionValue(LAMBDA_METADATA_FUNCTION_ARG); + checkArgument(metadataFunction.equals(metadataFunction.toLowerCase()), + "Lambda function name must be lowercase."); + + if (parsedArgs.hasOption(TABLE_ID_ARG)) { + checkArgument(parsedArgs.hasOption(SCHEMA_ID_ARG), + "The --schema argument must be provided if the --table argument is provided."); + } + + if (parsedArgs.hasOption(CONSTRAINTS_ARG)) { + checkArgument(parsedArgs.hasOption(TABLE_ID_ARG), + "The --table argument must be provided if the --constraints argument is provided."); + } + + String catalog = metadataFunction; + if (parsedArgs.hasOption(CATALOG_ID_ARG)) { + catalog = parsedArgs.getOptionValue(CATALOG_ID_ARG); + checkArgument(catalog.equals(catalog.toLowerCase()), + "Catalog name must be lowercase."); + } + + return new TestConfig(metadataFunction, + parsedArgs.hasOption(LAMBDA_RECORD_FUNCTION_ARG) + ? parsedArgs.getOptionValue(LAMBDA_RECORD_FUNCTION_ARG) + : metadataFunction, + catalog, + Optional.ofNullable(parsedArgs.getOptionValue(SCHEMA_ID_ARG)), + Optional.ofNullable(parsedArgs.getOptionValue(TABLE_ID_ARG)), + Optional.ofNullable(parsedArgs.getOptionValue(CONSTRAINTS_ARG)), + parsedArgs.hasOption(PLANNING_ONLY_ARG)); + } + } +} diff --git a/athena-federation-sdk-tools/src/main/java/com/amazonaws/athena/connector/validation/ConstraintParser.java b/athena-federation-sdk-tools/src/main/java/com/amazonaws/athena/connector/validation/ConstraintParser.java new file mode 100644 index 0000000000..239039ebd3 --- /dev/null +++ b/athena-federation-sdk-tools/src/main/java/com/amazonaws/athena/connector/validation/ConstraintParser.java @@ -0,0 +1,219 @@ +/*- + * #%L + * Amazon Athena Query Federation SDK Tools + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connector.validation; + +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.domain.predicate.Range; +import com.amazonaws.athena.connector.lambda.domain.predicate.SortedRangeSet; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import com.google.common.base.Splitter; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.arrow.vector.types.pojo.Schema; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +import static com.amazonaws.athena.connector.validation.ConnectorValidator.BLOCK_ALLOCATOR; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; + +/** + * This class provides the ability to transform a simple constraint grammar into an instance of {@link Constraints}. + */ +public class ConstraintParser +{ + private static final Splitter CONSTRAINT_SPLITTER = Splitter.on(','); + + private ConstraintParser() + { + // Intentionally left blank. + } + + private enum LogicalOperator + { + EQ("=", 0) + { + ValueSet createValueSet(ArrowType type, Object operand) + { + if (operand == null) { + return SortedRangeSet.newBuilder(type, true).build(); + } + return SortedRangeSet.of(false, Range.equal(BLOCK_ALLOCATOR, type, operand)); + } + }, + NEQ("!=", 1) + { + ValueSet createValueSet(ArrowType type, Object operand) + { + return EQ.createValueSet(type, operand).complement(BLOCK_ALLOCATOR); + } + }, + GT(">", 0) + { + ValueSet createValueSet(ArrowType type, Object operand) + { + return SortedRangeSet.of(false, Range.greaterThan(BLOCK_ALLOCATOR, type, operand)); + } + }, + GTE(">=", 1) + { + ValueSet createValueSet(ArrowType type, Object operand) + { + return SortedRangeSet.of(false, Range.greaterThanOrEqual(BLOCK_ALLOCATOR, type, operand)); + } + }, + LT("<", 0) + { + ValueSet createValueSet(ArrowType type, Object operand) + { + return SortedRangeSet.of(false, Range.lessThan(BLOCK_ALLOCATOR, type, operand)); + } + }, + LTE("<=", 1) + { + ValueSet createValueSet(ArrowType type, Object operand) + { + return SortedRangeSet.of(false, Range.lessThanOrEqual(BLOCK_ALLOCATOR, type, operand)); + } + }; + + private final String operator; + private final int rank; + + LogicalOperator(String operator, int rank) + { + this.operator = operator; + this.rank = rank; + } + + public String getOperator() + { + return operator; + } + + public int getRank() + { + return rank; + } + + abstract ValueSet createValueSet(ArrowType type, Object operand); + } + + /** + * This method takes in a table schema and a String representing the set of + * simple contraints to be ANDed together and applied to that table. + * + * @param schema The schema of the table in question + * @param input A comma-separated constraint String in the form of {field_name}{operator}{value}. + * The operators must be one of those available in {@link LogicalOperator}. + * Currently, we only support Boolean, Integer, Floating Point, Decimal, and String operands + * for this validator's constraints. + * @return a {@link Constraints} object populated from the input string, un-constrained if input is not present + */ + public static Constraints parseConstraints(Schema schema, Optional input) + { + if (!input.isPresent() || input.get().trim().isEmpty()) { + return new Constraints(Collections.EMPTY_MAP); + } + + Map fieldTypes = schema.getFields().stream() + .collect(Collectors.toMap(Field::getName, Field::getType)); + + Map constraints = new HashMap<>(); + Iterable constraintStrings = CONSTRAINT_SPLITTER.split(input.get()); + constraintStrings.forEach(str -> parseAndAddConstraint(fieldTypes, constraints, str)); + return new Constraints(constraints); + } + + private static void parseAndAddConstraint(Map fieldTypes, + Map constraints, + String constraintString) + { + LogicalOperator matchedOperator = null; + int bestMatchRank = Integer.MIN_VALUE; + for (LogicalOperator operator : LogicalOperator.values()) { + if (constraintString.contains(operator.getOperator()) && operator.getRank() > bestMatchRank) { + matchedOperator = operator; + bestMatchRank = operator.getRank(); + } + } + checkState(matchedOperator != null, + String.format("No operators found in constraint string '%s'!" + + " Allowable operators are %s", constraintString, + Arrays.stream(LogicalOperator.values()) + .map(LogicalOperator::getOperator) + .collect(Collectors.toList()))); + + String[] operands = constraintString.split(matchedOperator.getOperator()); + String fieldName = operands[0].trim(); + + if (fieldName == null) { + throw new IllegalArgumentException( + String.format("Constraint segment %s could not be parsed into a valid expression!" + + " Please use the form for each constraint.", + constraintString)); + } + + // If there is no reference value for this field, then we treat the right operand as null. + Object correctlyTypedOperand = null; + if (operands.length > 1) { + checkArgument(operands.length == 2, + String.format("Constraint argument %s contains multiple occurrences of operator %s", + constraintString, matchedOperator.getOperator())); + correctlyTypedOperand = tryParseOperand(fieldTypes.get(fieldName), operands[1]); + } + + constraints.put(fieldName, matchedOperator.createValueSet(fieldTypes.get(fieldName), correctlyTypedOperand)); + } + + /* + * Currently, we only support Boolean, Integer, Floating Point, Decimal, and String operands. + */ + private static Object tryParseOperand(ArrowType type, String operand) + { + switch (type.getTypeID()) { + case Bool: + return Boolean.valueOf(operand); + case FloatingPoint: + case Decimal: + try { + return Float.valueOf(operand); + } + catch (NumberFormatException floatEx) { + return Double.valueOf(operand); + } + case Int: + try { + return Integer.valueOf(operand); + } + catch (NumberFormatException floatEx) { + return Long.valueOf(operand); + } + default: + // For anything else, we try passing the operand as provided. + return operand; + } + } +} diff --git a/athena-federation-sdk-tools/src/main/java/com/amazonaws/athena/connector/validation/LambdaMetadataProvider.java b/athena-federation-sdk-tools/src/main/java/com/amazonaws/athena/connector/validation/LambdaMetadataProvider.java new file mode 100644 index 0000000000..1f7a42c755 --- /dev/null +++ b/athena-federation-sdk-tools/src/main/java/com/amazonaws/athena/connector/validation/LambdaMetadataProvider.java @@ -0,0 +1,259 @@ +/*- + * #%L + * Amazon Athena Query Federation SDK Tools + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connector.validation; + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesResponse; +import com.amazonaws.athena.connector.lambda.metadata.MetadataRequest; +import com.amazonaws.athena.connector.lambda.metadata.MetadataResponse; +import com.amazonaws.athena.connector.lambda.security.FederatedIdentity; +import com.amazonaws.athena.connector.lambda.serde.ObjectMapperFactory; +import com.amazonaws.regions.Regions; +import com.amazonaws.services.lambda.AWSLambdaClientBuilder; +import com.amazonaws.services.lambda.invoke.LambdaFunction; +import com.amazonaws.services.lambda.invoke.LambdaFunctionNameResolver; +import com.amazonaws.services.lambda.invoke.LambdaInvokerFactory; +import com.amazonaws.services.lambda.invoke.LambdaInvokerFactoryConfig; +import org.apache.arrow.vector.types.pojo.Schema; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Method; +import java.util.List; +import java.util.Set; +import java.util.UUID; + +import static com.amazonaws.athena.connector.validation.ConnectorValidator.BLOCK_ALLOCATOR; + +/** + * This class offers multiple convenience methods to retrieve metadata from a deployed Lambda. + */ +public class LambdaMetadataProvider +{ + private static final Logger log = LoggerFactory.getLogger(LambdaMetadataProvider.class); + private static final String UNKNOWN_SUFFIX = "_unknown"; + + private LambdaMetadataProvider() + { + // Intentionally left blank. + } + + /** + * This method builds and executes a ListSchemasRequest against the specified Lambda function. + * + * @param catalog the catalog name to be passed to Lambda + * @param metadataFunction the name of the Lambda function to call + * @param identity the identity of the caller + * @return the response + */ + public static ListSchemasResponse listSchemas(String catalog, + String metadataFunction, + FederatedIdentity identity) + { + String queryId = UUID.randomUUID().toString() + UNKNOWN_SUFFIX; + log.info("Submitting ListSchemasRequest with ID " + queryId); + + try (ListSchemasRequest request = + new ListSchemasRequest(identity, queryId, catalog)) { + log.info("Submitting request: {}", request); + ListSchemasResponse response = (ListSchemasResponse) getService(metadataFunction).getMetadata(request); + log.info("Received response: {}", response); + return response; + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * This method builds and executes a ListTablesRequest against the specified Lambda function. + * + * @param catalog the catalog name to be passed to Lambda + * @param schema the name of the contextual schema for the request + * @param metadataFunction the name of the Lambda function to call + * @param identity the identity of the caller + * @return the response + */ + public static ListTablesResponse listTables(String catalog, + String schema, + String metadataFunction, + FederatedIdentity identity) + { + String queryId = UUID.randomUUID().toString() + UNKNOWN_SUFFIX; + log.info("Submitting ListTablesRequest with ID " + queryId); + + try (ListTablesRequest request = + new ListTablesRequest(identity, queryId, catalog, schema)) { + log.info("Submitting request: {}", request); + ListTablesResponse response = (ListTablesResponse) getService(metadataFunction).getMetadata(request); + log.info("Received response: {}", response); + return response; + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * This method builds and executes a GetTableRequest against the specified Lambda function. + * + * @param catalog the catalog name to be passed to Lambda + * @param tableName the schema-qualified table name indicating which table should be retrieved + * @param metadataFunction the name of the Lambda function to call + * @param identity the identity of the caller + * @return the response + */ + public static GetTableResponse getTable(String catalog, + TableName tableName, + String metadataFunction, + FederatedIdentity identity) + { + String queryId = UUID.randomUUID().toString() + UNKNOWN_SUFFIX; + log.info("Submitting GetTableRequest with ID " + queryId); + + try (GetTableRequest request = + new GetTableRequest(identity, queryId, catalog, tableName)) { + log.info("Submitting request: {}", request); + GetTableResponse response = (GetTableResponse) getService(metadataFunction).getMetadata(request); + log.info("Received response: {}", response); + return response; + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * This method builds and executes a GetTableLayoutRequest against the specified Lambda function. + * + * @param catalog the catalog name to be passed to Lambda + * @param tableName the schema-qualified table name indicating the table whose layout should be retrieved + * @param constraints the constraints to be applied to the request + * @param schema the schema of the table in question + * @param partitionCols the partition column names for the table in question + * @param metadataFunction the name of the Lambda function to call + * @param identity the identity of the caller + * @return the response + */ + public static GetTableLayoutResponse getTableLayout(String catalog, + TableName tableName, + Constraints constraints, + Schema schema, + Set partitionCols, + String metadataFunction, + FederatedIdentity identity) + { + String queryId = UUID.randomUUID().toString() + UNKNOWN_SUFFIX; + log.info("Submitting GetTableLayoutRequest with ID " + queryId); + + try (GetTableLayoutRequest request = + new GetTableLayoutRequest(identity, queryId, catalog, tableName, constraints, schema, partitionCols)) { + log.info("Submitting request: {}", request); + GetTableLayoutResponse response = (GetTableLayoutResponse) getService(metadataFunction).getMetadata(request); + log.info("Received response: {}", response); + return response; + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * This method builds and executes a GetSplitsRequest against the specified Lambda function. + * + * @param catalog the catalog name to be passed to Lambda + * @param tableName the schema-qualified table name indicating the table for which splits should be retrieved + * @param constraints the constraints to be applied to the request + * @param partitions the block of partitions to be provided with the request + * @param partitionCols the partition column names for the table in question + * @param contToken a continuation token to be provided with the request, or null + * @param metadataFunction the name of the Lambda function to call + * @param identity the identity of the caller + * @return the response + */ + public static GetSplitsResponse getSplits(String catalog, + TableName tableName, + Constraints constraints, + Block partitions, + List partitionCols, + String contToken, + String metadataFunction, + FederatedIdentity identity) + { + String queryId = UUID.randomUUID().toString() + UNKNOWN_SUFFIX; + log.info("Submitting GetSplitsRequest with ID " + queryId); + + try (GetSplitsRequest request = + new GetSplitsRequest(identity, queryId, catalog, tableName, partitions, partitionCols, constraints, contToken)) { + log.info("Submitting request: {}", request); + GetSplitsResponse response = (GetSplitsResponse) getService(metadataFunction).getMetadata(request); + log.info("Received response: {}", response); + return response; + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + public interface MetadataService + { + @LambdaFunction + MetadataResponse getMetadata(final MetadataRequest request); + } + + public static final class Mapper + implements LambdaFunctionNameResolver + { + private final String metadataLambda; + + private Mapper(String metadataLambda) + { + this.metadataLambda = metadataLambda; + } + + @Override + public String getFunctionName(Method method, LambdaFunction lambdaFunction, + LambdaInvokerFactoryConfig lambdaInvokerFactoryConfig) + { + return metadataLambda; + } + } + + private static MetadataService getService(String lambdaFunction) + { + return LambdaInvokerFactory.builder() + .lambdaClient(AWSLambdaClientBuilder.standard().withRegion(Regions.US_EAST_2) + .build()) + .objectMapper(ObjectMapperFactory.create(BLOCK_ALLOCATOR)) + .lambdaFunctionNameResolver(new Mapper(lambdaFunction)) + .build(MetadataService.class); + } +} diff --git a/athena-federation-sdk-tools/src/main/java/com/amazonaws/athena/connector/validation/LambdaRecordProvider.java b/athena-federation-sdk-tools/src/main/java/com/amazonaws/athena/connector/validation/LambdaRecordProvider.java new file mode 100644 index 0000000000..8a4ea1e28d --- /dev/null +++ b/athena-federation-sdk-tools/src/main/java/com/amazonaws/athena/connector/validation/LambdaRecordProvider.java @@ -0,0 +1,137 @@ +/*- + * #%L + * Amazon Athena Query Federation SDK Tools + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connector.validation; + +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsResponse; +import com.amazonaws.athena.connector.lambda.records.RecordRequest; +import com.amazonaws.athena.connector.lambda.records.RecordResponse; +import com.amazonaws.athena.connector.lambda.security.FederatedIdentity; +import com.amazonaws.athena.connector.lambda.serde.ObjectMapperFactory; +import com.amazonaws.regions.Regions; +import com.amazonaws.services.lambda.AWSLambdaClientBuilder; +import com.amazonaws.services.lambda.invoke.LambdaFunction; +import com.amazonaws.services.lambda.invoke.LambdaFunctionNameResolver; +import com.amazonaws.services.lambda.invoke.LambdaInvokerFactory; +import com.amazonaws.services.lambda.invoke.LambdaInvokerFactoryConfig; +import org.apache.arrow.vector.types.pojo.Schema; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Method; +import java.util.UUID; + +import static com.amazonaws.athena.connector.validation.ConnectorValidator.BLOCK_ALLOCATOR; + +/** + * This class offers a convenience method to retrieve records from a deployed Lambda. + */ +public class LambdaRecordProvider +{ + private static final Logger log = LoggerFactory.getLogger(LambdaMetadataProvider.class); + + private static final long MAX_BLOCK_SIZE = 16000000; + private static final long MAX_INLINE_BLOCK_SIZE = 5242880; + + private LambdaRecordProvider() + { + // Intentionally left blank. + } + + /** + * This method builds and executes a ReadRecordsRequest against the specified Lambda function. + * + * @param catalog the catalog name to be passed to Lambda + * @param tableName the schema-qualified table name indicating the table for which splits should be retrieved + * @param constraints the constraints to be applied to the request + * @param schema the schema of the table in question + * @param split the split to be read in this request + * @param recordFunction the name of the Lambda function to call + * @param identity the identity of the caller + * @return the response + */ + public static ReadRecordsResponse readRecords(String catalog, + TableName tableName, + Constraints constraints, + Schema schema, + Split split, + String recordFunction, + FederatedIdentity identity) + { + String queryId = UUID.randomUUID().toString() + "_unknown"; + log.info("Submitting ReadRecordsRequest with ID " + queryId); + + try (ReadRecordsRequest request = + new ReadRecordsRequest(identity, + queryId, + catalog, + tableName, + schema, + split, + constraints, + MAX_BLOCK_SIZE, + MAX_INLINE_BLOCK_SIZE)) { + log.info("Submitting request: {}", request); + ReadRecordsResponse response = (ReadRecordsResponse) getService(recordFunction).readRecords(request); + log.info("Received response: {}", response); + return response; + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + public interface RecordService + { + @LambdaFunction + RecordResponse readRecords(final RecordRequest request); + } + + public static final class Mapper + implements LambdaFunctionNameResolver + { + private final String metadataLambda; + + private Mapper(String metadataLambda) + { + this.metadataLambda = metadataLambda; + } + + @Override + public String getFunctionName(Method method, LambdaFunction lambdaFunction, + LambdaInvokerFactoryConfig lambdaInvokerFactoryConfig) + { + return metadataLambda; + } + } + + private static RecordService getService(String lambdaFunction) + { + return LambdaInvokerFactory.builder() + .lambdaClient(AWSLambdaClientBuilder.standard().withRegion(Regions.US_EAST_2) + .build()) + .objectMapper(ObjectMapperFactory.create(BLOCK_ALLOCATOR)) + .lambdaFunctionNameResolver(new Mapper(lambdaFunction)) + .build(RecordService.class); + } +} diff --git a/athena-federation-sdk-tools/src/main/resources/log4j.properties b/athena-federation-sdk-tools/src/main/resources/log4j.properties new file mode 100644 index 0000000000..750e867d2b --- /dev/null +++ b/athena-federation-sdk-tools/src/main/resources/log4j.properties @@ -0,0 +1,26 @@ +### +# #%L +# Amazon Athena Query Federation SDK +# %% +# Copyright (C) 2019 Amazon Web Services +# %% +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# #L% +### +log = . +log4j.rootLogger = INFO, CONSOLE + +#Define the CONSOLE appender +log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender +log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout +log4j.appender.CONSOLE.layout.conversionPattern=%d{yyyy-MM-dd HH:mm:ss} <%X{AWSRequestId}> %-5p %c{1}:%m%n diff --git a/athena-federation-sdk/LICENSE.txt b/athena-federation-sdk/LICENSE.txt new file mode 100644 index 0000000000..418de4c108 --- /dev/null +++ b/athena-federation-sdk/LICENSE.txt @@ -0,0 +1,174 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. \ No newline at end of file diff --git a/athena-federation-sdk/README.md b/athena-federation-sdk/README.md new file mode 100644 index 0000000000..35ef092723 --- /dev/null +++ b/athena-federation-sdk/README.md @@ -0,0 +1,185 @@ +# Amazon Athena Query Federation SDK + +The Athena Query Federation SDK defines a set of interfaces and wire protocols that you can implement to enable Athena to delegate portions of it's query execution plan to code that you deploy/write. + +This essentially allows you to customize Athena's core execution engine with your own functionality while still taking advantage of Athena's ease of use and fully managed nature. + +You can find a collection of ready made modules that allow Athena to connect to various data sources by going to [Serverless Application Repository](https://console.aws.amazon.com/serverlessrepo/). Serverless Application Repository will allow you to search for and 1-Click deploy Athena connectors. + + Alternatively, you can explore [the Amazon Athena Query Federation github repositoru](https://github.com/awslabs/aws-athena-query-federation) for many of those same ready made connectors, modify them as you see fit, or write your own connector using the included example project. + +For those seeking to write their own connectors, we recommend you being by going through the [tutorial in athena-example](https://github.com/awslabs/aws-athena-query-federation/tree/master/athena-example) + +## Features + +* **Federated Metadata** - It is not always practical to centralize table metadata in a centralized meta-store. As such, this SDK allows Athena to delegate portions of its query planning to your connector in order to retrieve metadata about your data source. +* Glue DataCatalog Support - You can optionally enable a pre-built Glue MetadataHandler in your connector which will first attempt to fetch metadata from Glue about any table being queried before given you an opportunitiy to modify or re-write the retrieved metadata. This can be handy when you are using a custom format it S3 or if your data source doesn't have its own source of metadata (e.g. redis). +* **AWS Secrets Manager Integration** - If your connectors need passwords or other sensitive information, you can optionally use the SDK's built in tooling to resolve secrets. For example, if you have a config with a jdbc connection string you can do: "jdbc://${username}:${password}@hostname:post?options" and the SDK will automatically replace ${username} and ${password} with AWS Secrets Manager secrets of the same name. +* **Federated Identity** - When Athena federates a query to your connector, you may want to perform Authz based on the identitiy of the entity that executed the Athena Query. +* **Partition Pruning** - Athena will call you connector to understand how the table being queried is partitioned as well as to obtain which partitions need to be read for a given query. If your source supports partitioning, this give you an opportunity to use the query predicate to perform partition prunning. +* **Parallelized & Pipelined Reads** - Athena will parallelize reading your tables based on the partitioning information you provide. You also have the opportunity to tell Athena how (and if) it should split each partition into multiple (potentially concurrent) read operations. Behind the scenes Athena will parallelize reading the split (work units) you've created and pipeline reads to reduce the performance impact of reading a remote source. +* **Predicate Pushdown** - (Associative Predicates) Where relevant, Athena will supply you with the associative portion of the query predicate so that you can perform filtering or push the predicate into your source system for even better performance. It is important to note that the predicate is not always the query's full predicate. For example, if the query's predicate was "where (col0 < 1 or col1 < 10) and col2 + 10 < 100" only the "col0 < 1 or col1 < 10" will be supplied to you at this time. We are still considering the best form for supplying connectors with a more complete view of the query and its predicate and expect a future release to provide this to connectors that are capable of utilizing +* **Column Projection** - Where relevant, Athena will supply you with the columns that need to be projected so that you can reduce data scanned. +* **Limited Scans** - While Athena is not yet able to push down limits to you connector, the SDK does expose a mechanism by which you can abandon a scan early. Athena will already avoid scanning partitions and splits that are not needed once a limit, failure, or user cancellation occurs but this functionality will allow connectors that are in the middle of processing a split to stop regardless of the cause. This works even when the query's limit can not be semantically pushed down (e.g. limit happens after a filtered join). In a future release we may also introduce traditional limit pushdwon for the simple cases that would support that. +* **Congestion Control** - Some of the source you may wish to federate to may not be as scalable as Athena or may be running performance sensitive workloads that you wish to protect from an overzealous federated query. Athena will automatically detect congestion by listening for FederationThrottleException(s) as well as many other AWS service exceptions that indicate your source is overwhelmed. When Athena detects congestion it reducing parallelism against your source. Within the SDK you can make use of ThrottlingInvoker to more tightly control congestion yourself. Lastly, you can reduce the concurrency your Lambda functions are allowed to achieve in the Lambda console and Athena will respect that setting. + +### DataTypes + +The wire protocol between your connector(s) and Athena is built on Apache Arrow with JSON for request/response structures. As such we make use of Apache Arrow's type system. At this time we support the below Apache Arrow types with plans to add more (e.g. timestamp w/TZ and Map are some of the upcoming additions) + +The below table lists the supported Apache Arrow types as well as the corresponding java type you can use to 'set' values via Block.setValue(...) or BlockUtils.setValue(...). It is important to remember that while this SDK offers a number of convenience helpers to make working with Apache Arrow easier for the beginner you always have the option of using Apache Arrow directly. Using Arrow Directly can offer improved performance as well as more options for how you handle type conversion and coercion. + +|Apache Arrow Data Type|Java Type| +|-------------|-----------------| +|BIT|int, boolean| +|DATEMILLI|Date, long, int| +|DATEDAY|Date, long, int| +|FLOAT8|double| +|FLOAT4|float| +|INT|int, long| +|TINYINT|int| +|SMALLINT|int| +|BIGINT|long| +|VARBINARY|byte[]| +|DECIMAL|double, BigDecimal| +|VARCHAR|String, Text| +|STRUCT|Object (w/ FieldResolver)| +|LIST|iterable (w/Optional FieldResolver)| + + +## What is a 'Connector'? + +A 'Connector' is a piece of code that understands how to execute portions of an Athena query outside of Athena's core engine. Connectors must satisfy a few basic requirements. + +1. Your connector must provide a source of meta-data for Athena to get schema information about what databases, tables, and columns your connector has. This is done by building and deploying a lambda function that extends or composes com.amazonaws.athena.connector.lambda.handlers.MetadataHandler in the athena-federation-sdk module. +2. Your connector must provide a way for Athena to read the data stored in your tables. This is done by building and deploying a lambda function that extends or composes com.amazonaws.athena.connector.lambda.handlers.RecordHandler in the athena-federation-sdk module. + +Alternatively, you can deploy a single Lambda function which combines the two above requirements by using com.amazonaws.athena.connector.lambda.handlers.CompositeHandler or com.amazonaws.athena.connector.lambda.handlers.UnifiedHandler. While breaking this into two separate Lambda functions allows you to independently control the cost and timeout of your Lambda functions, using a single Lambda function can be simpler and higher performance due to less cold start. + +In the next section we take a closer look at the methods we must implement on the MetadataHandler and RecordHandler. + +Included with this SDK is a set of examples in src/com/amazonaws/athena/connector/lambda/examples . You can deploy the examples using the included athena-federation-sdk.yaml file. Run `../tools/publish.sh S3_BUCKET_NAME athena-federation-sdk` to publish the connector to your private AWS Serverless Application Repository. This will allow users with permission to do so, the ability to deploy instances of the connector via 1-Click form in the Serverless Application Repository console. After you've used the Serverless Application Repository UX to deploy and instance of your connection you can run the validation script `../tools/validate_connector.sh ` be sure to replace with the name you gave to your function/catalog when you deployed it via Serverless Application Repository. To ensure you connector is valid before running an Athena query. For detailed steps on building and deploying please view the README.md in the athena-example module of this repository. + +You can then run `SELECT count(*) from "lambda:"."custom_source"."fake_table" where year > 2010` from your Athena console. Be sure you replace with the catalog name you gave your connector when you deployed it. + + +### MetadataHandler Details + + Below we have the basic functions we need to implement when using the Amazon Athena Query Federation SDK's MetadataHandler to satisfy the boiler plate work of serialization and initialization. The abstract class we are extending takes care of all the Lambda interface bits and delegates only the discrete operations that are relevant to the task at hand, querying our new data source. + +All schema names, table names, and column names must be lower case at this time. Any entities that are uppercase or mixed case will not be accessible in queries and will be lower cased by Athena's engine to ensure consistency across sources. As such you may need to handle this when integrating with a source that supports mixed case. As an example, you can look at the CloudwatchTableResolver in the athena-cloudwatch module for one potential approach to this challenge. + +```java +public class MyMetadataHandler extends MetadataHandler +{ + @Override + protected ListSchemasResponse doListSchemaNames(BlockAllocator blockAllocator, ListSchemasRequest request) + { + //Return a list of Schema names (strings) for the requested catalog + } + + @Override + protected ListTablesResponse doListTables(BlockAllocator blockAllocator, ListTablesRequest request) + { + //Return a list of tables (strings) for the requested catalog and schema + } + + @Override + protected GetTableResponse doGetTable(BlockAllocator blockAllocator, GetTableRequest request) + { + //Return a table (column names, types, descriptions and table properties) + } + + @Override + public void getPartitions(BlockWriter blockWriter, GetTableLayoutRequest request) { + //Generates the partitions of the requested table that need to be read + //to satisfy the supplied predicate. This is meant to be a fast pruning operation. + //Source that don't support partitioning can return a single partition. Partitions + //are opaque to Athena and are just used to call the next method, doGetSplits(...) + //Partition Pruning is automatically handled by BlockWriter which creates + //Blocks that are constrained to filter out values that do not match + //the requests constraints. You can optionally get a ConstraintEvaluator + //from the BlockWriter or get constraints directly from the request if you + //need to do some customer filtering for performance reasons or to push + //down into your source system. + } + + @Override + protected GetSplitsResponse doGetSplits(BlockAllocator blockAllocator, GetSplitsRequest request) + { + //Return the Split(s) that define how reading your the requested table can be parallelized. + //Think of this method as a work-producer. Athena will call this paginated API while also + //scheduling each Split for execution. Sources that don't support parallelism can return + //a single split. Splits are mostly opaque to Athena and are just used to call your RecordHandler. + } +} +``` + +You can find example MetadataHandlers by looking at some of the connectors in the repository. athena-cloudwatch and athena-tpcds are fairly easy to follow along with. + +Alternatively, if you wish to use AWS Glue DataCatalog as the authoritative (or supplemental) source of meta-data for your connector you can extend com.amazonaws.athena.connector.lambda.handlers.GlueMetadataHandler instead of com.amazonaws.athena.connector.lambda.handlers.MetadataHandler. GlueMetadataHandler comes with implementations for doListSchemas(...), doListTables(...), and doGetTable(...) leaving you to implemented only 2 methods. The Amazon Athena DocumentDB Connector in the athena-docdb module is an example of using GlueMetadataHandler. + +### RecordHandler Details + +Lets take a closer look at what is required for a RecordHandler. Below we have the basic functions we need to implement when using the Amazon Athena Query Federation SDK's MetadataHandler to satisfy the boiler plate work of serialization and initialization. The abstract class we are extending takes care of all the Lambda interface bits and delegates on the discrete operations that are relevant to the task at hand, querying our new data source. + +```java +public class MyRecordHandler + extends RecordHandler +{ + @Override + protected void readWithConstraint(ConstraintEvaluator constraintEvaluator, + BlockSpiller blockSpiller, + ReadRecordsRequest request) + { + //read the data represented by the Split in the request and use the blockSpiller.writeRow() + //to write rows into the response. The Amazon Athena Query Federation SDK handles all the + //boiler plate of spilling large response to S3, and optionally encrypting any spilled data. + //If you source supports filtering, use the Constraints objects on the request to push the predicate + //down into your source. You can also use the provided ConstraintEvaluator to performing filtering + //in this code block. + } +} +``` + +## Performance + +Federated queries may run more slowly than queries which are 100% localized to Athena's execution engine, however much of this is dependent upon the source you are interacting with. +When running a federated query, Athena make use of a deep execution pipeline as well as various data pre-fetch techniques to hide the performance impact of doing remote reads. If +your source supports parallel scans and predicate push-down it is possible to achieve performance that is close to that of native Athena. + +To put some real work context around this, we tested this SDK as well as the usage of AWS Lambda by re-creating Athena's S3 + AWS Glue integration as a federated connector. We then +ran 2 tests using a highly (~3000 files totaling 350GB) parallelizable dataset on S3. The tests were a select count(*) from test_table where our test table had 4 columns of +primitive types (int, bigint, float4, float8). This query was purposely simple because we wanted to stress test the TABLE_SCAN operation which corresponds very closely to the +current capabilities of our connector. We expect most workloads, for parallelizable source tables, to bottleneck on other areas of query execution before running into constraints +associated with federated TABLE_SCAN performance. + +|Test|GB/Sec|Rows/Sec| +|-------------|-----------------|-------------| +|Federated S3 Query w/Apache Arrow|102 Gbps|1.5B rows/sec| +|Athena + TextCSV on S3 Query|115 Gbps|120M rows/sec| +|Athena + Parquet on S3 Query|30Gbps*|2.7B rows/sec| + +*Parquet's run-length encoding makes the GB/sec number somewhat irrelevant for comparison testing but since it is more compact than Apache Arrow it does mean lower network utilization. +**These are not exhaustive tests but rather represent the point at which we stopped validation testing. + + +### Throttling & Rate Limiting + +If your Lambda function(s) throw a FederationThrottleException or if Lambda/EC2 throws a limit exceed exception, Athena will use that as an indication that your Lambda function(s) +or the source they talk to are under too much load and trigger Athena's Additive-Increase/Multiplicative-Decrease based Congestion Control mechanism. Some sources may generate +throttling events in the middle of a Lambda invocation, after some data has already been returned. In these cases, Athena can not always automatically apply congestion control +because retrying the call may lead to incorrect query results. We recommend using ThrottlingInvoker to handle calls to depedent services in your connector. The ThrottlingInvoker +has hooks to see if you've already written rows to the response and thus decide how best to handle a Throttling event either by: sleeping and retrying in your Lamnbda function or +by bubbling up a FederationThrottleException to Athena. + +You can configure ThrottlingInvoker via its builder or for pre-built connectors like athena-cloudwatch by setting the following environment variables: + +1. **throttle_initial_delay_ms** - (Default: 10ms) This is the initial call delay applied after the first congestion event. +1. **throttle_max_delay_ms** - (Default: 1000ms) This is the max delay between calls. You can derive TPS by dividing it into 1000ms. +1. **throttle_decrease_factor** - (Default: 0.5) This is the factor by which we reduce our call rate. +1. **throttle_increase_ms** - (Default: 10ms) This is the rate at which we decrease the call delay. + +## License + +This project is licensed under the Apache-2.0 License. \ No newline at end of file diff --git a/athena-federation-sdk/athena-federation-sdk.yaml b/athena-federation-sdk/athena-federation-sdk.yaml new file mode 100644 index 0000000000..538bab6327 --- /dev/null +++ b/athena-federation-sdk/athena-federation-sdk.yaml @@ -0,0 +1,58 @@ +Transform: 'AWS::Serverless-2016-10-31' +Metadata: + 'AWS::ServerlessRepo::Application': + Name: AthenaDataGeneratorConnector + Description: 'This connector enables Amazon Athena to communicate with a randomly generated data source.' + Author: 'Amazon Athena' + SpdxLicenseId: Apache-2.0 + LicenseUrl: LICENSE.txt + ReadmeUrl: README.md + Labels: + - athena-federation + HomePageUrl: 'https://github.com/awslabs/aws-athena-query-federation' + SemanticVersion: 1.0.0 + SourceCodeUrl: 'https://github.com/awslabs/aws-athena-query-federation' +Parameters: + AthenaCatalogName: + Description: 'The name you will give to this catalog in Athena. It will also be used as the function name.' + Type: String + SpillBucket: + Description: 'The bucket where this function can spill data.' + Type: String + SpillPrefix: + Description: 'The bucket prefix where this function can spill large responses.' + Type: String + Default: athena-spill + LambdaTimeout: + Description: 'Maximum Lambda invocation runtime in seconds. (min 1 - 900 max)' + Default: 900 + Type: Number + LambdaMemory: + Description: 'Lambda memory in MB (min 128 - 3008 max).' + Default: 3008 + Type: Number + DisableSpillEncryption: + Description: "WARNING: If set to 'true' encryption for spilled data is disabled." + Default: 'false' + Type: String +Resources: + ConnectorConfig: + Type: 'AWS::Serverless::Function' + Properties: + Environment: + Variables: + disable_spill_encryption: !Ref DisableSpillEncryption + spill_bucket: !Ref SpillBucket + spill_prefix: !Ref SpillPrefix + FunctionName: !Ref AthenaCatalogName + Handler: "com.amazonaws.athena.connector.lambda.examples.ExampleCompositeHandler" + CodeUri: "./target/aws-athena-federation-sdk-2019.46.1-withdep.jar" + Description: "This connector enables Amazon Athena to communicate with a randomly generated data source." + Runtime: java8 + Timeout: !Ref LambdaTimeout + MemorySize: !Ref LambdaMemory + Policies: + #S3CrudPolicy allows our connector to spill large responses to S3. You can optionally replace this pre-made policy + #with one that is more restrictive and can only 'put' but not read,delete, or overwrite files. + - S3CrudPolicy: + BucketName: !Ref SpillBucket \ No newline at end of file diff --git a/athena-federation-sdk/checkstyle.xml b/athena-federation-sdk/checkstyle.xml new file mode 100644 index 0000000000..c79e1ac630 --- /dev/null +++ b/athena-federation-sdk/checkstyle.xml @@ -0,0 +1,167 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/athena-federation-sdk/pom.xml b/athena-federation-sdk/pom.xml new file mode 100644 index 0000000000..bf8a2fae57 --- /dev/null +++ b/athena-federation-sdk/pom.xml @@ -0,0 +1,235 @@ + + + + 4.0.0 + + com.amazonaws + aws-athena-federation-sdk + 2019.47.1 + jar + Amazon Athena Query Federation SDK + + + 1.8 + 1.8 + 1.7.1 + + + + Amazon Web Services + https://https://aws.amazon.com// + + + 2019 + + + + Apache License 2.0 + http://www.apache.org/licenses/LICENSE-2.0 + repo + + + + + + com.amazonaws + aws-java-sdk-secretsmanager + 1.11.490 + + + com.amazonaws + aws-java-sdk-glue + 1.11.490 + + + com.amazonaws + aws-java-sdk-athena + 1.11.490 + + + org.apache.arrow + arrow-vector + 0.11.0 + + + org.apache.arrow + arrow-memory + 0.11.0 + + + com.amazonaws + aws-lambda-java-core + 1.2.0 + + + com.amazonaws + aws-java-sdk-lambda + 1.11.490 + + + com.amazonaws + aws-java-sdk-s3 + 1.11.490 + + + com.amazonaws + aws-java-sdk-kms + 1.11.490 + + + com.google.guava + guava + 21.0 + + + + org.slf4j + slf4j-api + ${slf4jVersion} + + + + org.slf4j + jcl-over-slf4j + ${slf4jVersion} + + + org.slf4j + slf4j-log4j12 + 1.7.25 + + + com.amazonaws + aws-lambda-java-log4j + 1.0.0 + + + + org.bouncycastle + bcprov-jdk15on + 1.61 + + + + junit + junit + 4.12 + test + + + + org.mockito + mockito-all + 1.10.19 + test + + + + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + 3.1.0 + + checkstyle.xml + UTF-8 + true + false + false + + + + validate + validate + + check + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.1.1 + + true + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + withdep + package + + shade + + + + withdep + + + + with-arrow + package + + shade + + + + with-arrow + true + + + com.amazonaws.athena:* + org.apache.arrow:* + + + + + + + + + org.codehaus.mojo + license-maven-plugin + 2.0.0 + + false + false + false + + + + first + + update-file-header + + process-sources + + apache_v2 + + src/main/java + src/test + + + + + + + + \ No newline at end of file diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/CollectionsUtils.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/CollectionsUtils.java new file mode 100644 index 0000000000..3710372442 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/CollectionsUtils.java @@ -0,0 +1,45 @@ +package com.amazonaws.athena.connector.lambda; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import java.util.Collection; + +public class CollectionsUtils +{ + private CollectionsUtils() {} + + public static boolean equals(Collection lhs, Collection rhs) + { + if (lhs == null && rhs == null) { + return true; + } + + if ((lhs == null && rhs != null) || (lhs != null && rhs == null)) { + return false; + } + + if (lhs.size() != rhs.size()) { + return false; + } + + return lhs.containsAll(rhs); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/QueryStatusChecker.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/QueryStatusChecker.java new file mode 100644 index 0000000000..62ee98b71a --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/QueryStatusChecker.java @@ -0,0 +1,134 @@ +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connector.lambda; + +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.athena.model.GetQueryExecutionRequest; +import com.amazonaws.services.athena.model.GetQueryExecutionResult; +import com.amazonaws.services.athena.model.InvalidRequestException; +import com.google.common.collect.ImmutableSet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Set; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicBoolean; + +import static java.lang.String.format; + +/** + * This class provides a mechanism for callers to terminate in-progress work if the upstream Athena query waiting for that work has + * already terminated. Callers using the SDK as-is should only need to call #isQueryRunning, as #startQueryStatusChecker + * should have already been called by {@link com.amazonaws.athena.connector.lambda.handlers.MetadataHandler} or + * {@link com.amazonaws.athena.connector.lambda.handlers.RecordHandler}. + */ +public class QueryStatusChecker + implements AutoCloseable +{ + private static final Logger logger = LoggerFactory.getLogger(QueryStatusChecker.class); + + // progressively longer delays at which to poll + private static final int[] FIBONACCI = new int[] { 1, 1, 2, 3, 5, 8, 13, 21, 34, 55}; + // Athena terminal states + private static final Set TERMINAL_STATES = ImmutableSet.of("SUCCEEDED", "FAILED", "CANCELLED"); + + private boolean wasStarted = false; + private final AtomicBoolean isRunning = new AtomicBoolean(true); + private final AmazonAthena athena; + private final ThrottlingInvoker athenaInvoker; + private final String queryId; + private final Thread checkerThread; + + public QueryStatusChecker(AmazonAthena athena, ThrottlingInvoker athenaInvoker, String queryId) + { + this.athena = athena; + this.athenaInvoker = athenaInvoker; + this.queryId = queryId; + this.checkerThread = new Thread(() -> runQueryStatusChecker(queryId), "QueryStatusCheckerThread-" + queryId); + } + + /** + * Returns whether the query is still running + */ + public boolean isQueryRunning() + { + // start the checker thread if it hasn't started already + if (!wasStarted) { + synchronized (this) { + if (!wasStarted) { + checkerThread.start(); + wasStarted = true; + } + } + } + return isRunning.get(); + } + + /** + * Stops the status checker thread + */ + @Override + public void close() + { + // fine if the thread isn't running + checkerThread.interrupt(); + logger.debug("Interrupt signal sent to status checker thread"); + } + + private void runQueryStatusChecker(String queryId) + { + int attempt = 0; + while (isRunning.get()) { + int delay = FIBONACCI[Math.min(attempt, FIBONACCI.length - 1)]; + try { + Thread.sleep(delay * 1000); + checkStatus(queryId, attempt); + } + catch (InterruptedException e) { + logger.debug("Checker thread interrupted. Ceasing status polling"); + return; + } + attempt++; + } + logger.debug("Query terminated. Ceasing status polling"); + } + + private void checkStatus(String queryId, int attempt) + throws InterruptedException + { + logger.debug(format("Background thread checking status of Athena query %s, attempt %d", queryId, attempt)); + try { + GetQueryExecutionResult queryExecution = athenaInvoker.invoke(() -> athena.getQueryExecution(new GetQueryExecutionRequest().withQueryExecutionId(queryId))); + String state = queryExecution.getQueryExecution().getStatus().getState(); + if (TERMINAL_STATES.contains(state)) { + logger.debug("Query {} has terminated with state {}", queryId, state); + isRunning.set(false); + } + } + catch (RuntimeException | TimeoutException e) { + logger.warn("Exception {} thrown when calling Athena for query status: {}", e.getClass().getSimpleName(), e.getMessage()); + if (e instanceof InvalidRequestException) { + // query does not exist, so no need to keep calling Athena + logger.debug("Athena reports query {} not found. Interrupting checker thread", queryId); + throw new InterruptedException(); + } + } + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/ThrottlingInvoker.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/ThrottlingInvoker.java new file mode 100644 index 0000000000..fc1fa99628 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/ThrottlingInvoker.java @@ -0,0 +1,278 @@ +package com.amazonaws.athena.connector.lambda; + +/*- + * #%L + * athena-cloudwatch + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.BlockSpiller; +import com.amazonaws.athena.connector.lambda.exceptions.FederationThrottleException; +import org.apache.arrow.util.VisibleForTesting; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.Callable; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; + +/** + * + */ +public class ThrottlingInvoker +{ + private static final Logger logger = LoggerFactory.getLogger(ThrottlingInvoker.class); + + private static final String THROTTLE_INITIAL_DELAY_MS = "throttle_initial_delay_ms"; + private static final String THROTTLE_MAX_DELAY_MS = "throttle_max_delay_ms"; + private static final String THROTTLE_DECREASE_FACTOR = "throttle_decrease_factor"; + private static final String THROTTLE_INCREASE_MS = "throttle_increase_ms"; + + private static final long DEFAULT_INITIAL_DELAY_MS = 10; + private static final long DEFAULT_MAX_DELAY_MS = 1_000; + private static final double DEFAULT_DECREASE_FACTOR = 0.5D; + private static final long DEFAULT_INCREASE_MS = 10; + + private final long initialDelayMs; + private final long maxDelayMs; + private final double decrease; + private final long increase; + private final ExceptionFilter filter; + private final AtomicReference spillerRef; + private final AtomicLong delay = new AtomicLong(0); + private volatile State state = State.FAST_START; + + public enum State + {FAST_START, CONGESTED, AVOIDANCE} + + public interface ExceptionFilter + { + boolean isMatch(Exception ex); + } + + public ThrottlingInvoker(Builder builder) + { + this(builder.initialDelayMs, + builder.maxDelayMs, + builder.decrease, + builder.increase, + builder.filter, + builder.spiller); + } + + public ThrottlingInvoker(long initialDelayMs, + long maxDelayMs, + double decrease, + long increase, + ExceptionFilter filter, + BlockSpiller spiller) + { + if (decrease > 1 || decrease < .001) { + throw new IllegalArgumentException("decrease was " + decrease + " but should be between .001 and 1"); + } + + if (maxDelayMs < 1) { + throw new IllegalArgumentException("maxDelayMs was " + maxDelayMs + " but must be >= 1"); + } + + if (increase < 1) { + throw new IllegalArgumentException("increase was " + increase + " but must be >= 1"); + } + + this.initialDelayMs = initialDelayMs; + this.maxDelayMs = maxDelayMs; + this.decrease = decrease; + this.increase = increase; + this.filter = filter; + this.spillerRef = new AtomicReference<>(spiller); + } + + public static Builder newBuilder() + { + return new Builder(); + } + + public static Builder newDefaultBuilder(ExceptionFilter filter) + { + long initialDelayMs = (System.getenv(THROTTLE_INITIAL_DELAY_MS) != null) ? + Long.parseLong(System.getenv(THROTTLE_INITIAL_DELAY_MS)) : DEFAULT_INITIAL_DELAY_MS; + long maxDelayMs = (System.getenv(THROTTLE_MAX_DELAY_MS) != null) ? + Long.parseLong(System.getenv(THROTTLE_MAX_DELAY_MS)) : DEFAULT_MAX_DELAY_MS; + double decreaseFactor = (System.getenv(THROTTLE_DECREASE_FACTOR) != null) ? + Long.parseLong(System.getenv(THROTTLE_DECREASE_FACTOR)) : DEFAULT_DECREASE_FACTOR; + long increase = (System.getenv(THROTTLE_INCREASE_MS) != null) ? + Long.parseLong(System.getenv(THROTTLE_INCREASE_MS)) : DEFAULT_INCREASE_MS; + + return newBuilder() + .withInitialDelayMs(initialDelayMs) + .withMaxDelayMs(maxDelayMs) + .withDecrease(decreaseFactor) + .withIncrease(increase) + .withFilter(filter); + } + + public T invoke(Callable callable) + throws TimeoutException + { + return invoke(callable, 0); + } + + public T invoke(Callable callable, long timeoutMillis) + throws TimeoutException + { + long startTime = System.currentTimeMillis(); + do { + try { + applySleep(); + T result = callable.call(); + handleAvoidance(); + return result; + } + catch (Exception ex) { + if (!filter.isMatch(ex)) { + //The exception did not match our filter for congestion, throw + throw (ex instanceof RuntimeException) ? (RuntimeException) ex : new RuntimeException(ex); + } + handleThrottle(ex); + } + } + while (!isTimedOut(startTime, timeoutMillis)); + + throw new TimeoutException("Timed out before call succeeded after " + (System.currentTimeMillis() - startTime) + " ms"); + } + + public void setBlockSpiller(BlockSpiller spiller) + { + spillerRef.set(spiller); + } + + public State getState() + { + return state; + } + + @VisibleForTesting + protected long getDelay() + { + return delay.get(); + } + + private synchronized void handleThrottle(Exception ex) + { + if (spillerRef.get() != null && !spillerRef.get().spilled()) { + //If no blocks have spilled, it is better to signal the Throttle to Athena by propagating. + throw new FederationThrottleException("ThrottlingInvoker requesting slow down due to " + ex, ex); + } + + long newDelay = (long) Math.ceil(delay.get() / decrease); + if (newDelay == 0) { + newDelay = initialDelayMs; + } + else if (newDelay > maxDelayMs) { + newDelay = maxDelayMs; + } + logger.info("handleThrottle: Encountered a Throttling event[{}] adjusting delay to {} ms @ {} TPS", + ex, newDelay, 1000D / newDelay); + state = State.CONGESTED; + delay.set(newDelay); + } + + private synchronized void handleAvoidance() + { + long newDelay = delay.get() - increase; + if (newDelay <= 0) { + newDelay = 0; + } + + if (delay.get() > 0) { + state = State.AVOIDANCE; + logger.info("handleAvoidance: Congestion AVOIDANCE active, decreasing delay to {} ms @ {} TPS", + newDelay, (newDelay > 0) ? 1000 / newDelay : "unlimited"); + delay.set(newDelay); + } + } + + private void applySleep() + { + if (delay.get() > 0) { + try { + Thread.sleep(delay.get()); + } + catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + throw new RuntimeException(ex); + } + } + } + + private boolean isTimedOut(long startTime, long timeoutMillis) + { + return (timeoutMillis > 0) ? System.currentTimeMillis() - startTime > timeoutMillis : false; + } + + public static class Builder + { + private long initialDelayMs; + private long maxDelayMs; + private double decrease; + private long increase; + private ExceptionFilter filter; + private BlockSpiller spiller; + + public Builder withInitialDelayMs(long initialDelayMs) + { + this.initialDelayMs = initialDelayMs; + return this; + } + + public Builder withMaxDelayMs(long maxDelayMs) + { + this.maxDelayMs = maxDelayMs; + return this; + } + + public Builder withDecrease(double decrease) + { + this.decrease = decrease; + return this; + } + + public Builder withIncrease(long increase) + { + this.increase = increase; + return this; + } + + public Builder withFilter(ExceptionFilter filter) + { + this.filter = filter; + return this; + } + + public Builder withSpiller(BlockSpiller spiller) + { + this.spiller = spiller; + return this; + } + + public ThrottlingInvoker build() + { + return new ThrottlingInvoker(this); + } + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/ArrowTypeComparator.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/ArrowTypeComparator.java new file mode 100644 index 0000000000..dcca79545d --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/ArrowTypeComparator.java @@ -0,0 +1,109 @@ +package com.amazonaws.athena.connector.lambda.data; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import org.apache.arrow.vector.complex.reader.FieldReader; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.bouncycastle.util.Arrays; +import org.joda.time.LocalDateTime; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.math.BigDecimal; + +/** + * This utility class can be used to implement a comparator for various Apache Arrow typed values. It is mostly + * used as part of our testing harness and notably does not support certain complex types (e.g. STRUCTs). + */ +public class ArrowTypeComparator +{ + private static final Logger logger = LoggerFactory.getLogger(ArrowTypeComparator.class); + + private ArrowTypeComparator() {} + + public static int compare(FieldReader reader, Object lhs, Object rhs) + { + return compare(reader.getField().getType(), lhs, rhs); + } + + //TODO: Add support for Struct + public static int compare(ArrowType arrowType, Object lhs, Object rhs) + { + if (lhs == null && rhs == null) { + return 0; + } + else if (lhs == null) { + return 1; + } + else if (rhs == null) { + return -1; + } + + Types.MinorType type = Types.getMinorTypeForArrowType(arrowType); + switch (type) { + case INT: + case UINT4: + return Integer.compare((int) lhs, (int) rhs); + case TINYINT: + case UINT1: + return Byte.compare((byte) lhs, (byte) rhs); + case SMALLINT: + return Short.compare((short) lhs, (short) rhs); + case UINT2: + return Character.compare((char) lhs, (char) rhs); + case BIGINT: + case UINT8: + return Long.compare((long) lhs, (long) rhs); + case FLOAT8: + return Double.compare((double) lhs, (double) rhs); + case FLOAT4: + return Float.compare((float) lhs, (float) rhs); + case VARCHAR: + return lhs.toString().compareTo(rhs.toString()); + case VARBINARY: + return Arrays.compareUnsigned((byte[]) lhs, (byte[]) rhs); + case DECIMAL: + return ((BigDecimal) lhs).compareTo((BigDecimal) rhs); + case BIT: + return Boolean.compare((boolean) lhs, (boolean) rhs); + case DATEMILLI: + return ((LocalDateTime) lhs).compareTo((LocalDateTime) rhs); + case DATEDAY: + return ((Integer) lhs).compareTo((Integer) rhs); + case LIST: + //This could lead to thrashing if used to sort a collection + if (lhs.equals(rhs)) { + return 0; + } + else if (lhs.hashCode() < rhs.hashCode()) { + return -1; + } + else { + return 1; + } + default: + //logging because throwing in a comparator gets swallowed in many unit tests that use equality asserts + logger.warn("compare: Unknown type " + type + " object: " + lhs.getClass()); + throw new IllegalArgumentException("Unknown type " + type + " object: " + lhs.getClass()); + } + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/Block.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/Block.java new file mode 100644 index 0000000000..0377d6866e --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/Block.java @@ -0,0 +1,518 @@ +package com.amazonaws.athena.connector.lambda.data; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.domain.predicate.ConstraintEvaluator; +import com.google.common.base.MoreObjects; +import org.apache.arrow.vector.FieldVector; +import org.apache.arrow.vector.VectorLoader; +import org.apache.arrow.vector.VectorSchemaRoot; +import org.apache.arrow.vector.VectorUnloader; +import org.apache.arrow.vector.complex.reader.FieldReader; +import org.apache.arrow.vector.ipc.message.ArrowRecordBatch; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.arrow.vector.types.pojo.Schema; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.beans.Transient; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static com.amazonaws.athena.connector.lambda.data.BlockUtils.fieldToString; +import static java.util.Objects.requireNonNull; + +/** + * This class is used to provide a convenient interface for working (reading/writing) Apache Arrow Batches. As such + * this class is mostly a holder for an Apache Arrow Schema and the associated VectorSchema (used for read/write). + * The class also includes helper functions for easily loading/unloading data in the form of Arrow Batches. + * + * @note While using this class as a holder to encapsulate nuances of Apache Arrow can simplify your programming model + * and make it easier to get started, using setValue(...), setComplexValue(...), and any of the related helpers to + * write data to the Apache Arrow structures is less performant than using Apache Arrow's native interfaces. If your usecase + * and source data can be read in a columnar fashion you can achieve significantly (50% - 200%) better performance by + * avoiding setValue(...) and setComplexValue(...). In our testing conversion to Apache Arrow was not a significant + * bottleneck and instead represented extra latency which could be hidden through parallelism and pipelining. This is why + * we opted to offer these convenience methods. + *

+ * Remember to always close your Block(s) when you are done with them. If you are using a BlockAllocator it is still + * recommended that you close() Blocks explicitly wherever possible vs. depending on BlockAllocator.close() to free + * resources. Closing Blocks earlier will reduce peak memory demands and reduce the chance that you exhaust your Apache + * Arrow memory pool. + */ +public class Block + extends SchemaAware + implements AutoCloseable +{ + private static final Logger logger = LoggerFactory.getLogger(Block.class); + + //Used to identify which BlockAllocator owns the underlying memory resources used in this Block for debugging purposes. + //Not included in equality or hashcode. + private final String allocatorId; + //The schema of the block + private final Schema schema; + //The VectorSchemaRoot which can be used to read/write values to/from the underlying Apache Arrow buffers that + //for the Arrow Batch of rows. + private final VectorSchemaRoot vectorSchema; + //Used to constrain writes to the block, be default we use an emptyEvaluator that allows all writes. + //Note that we will _NOT_ close this ConstraintEvaluator because we may not own it and the emptyEvaluator + //has no resources that could leak. + private ConstraintEvaluator constraintEvaluator = ConstraintEvaluator.emptyEvaluator(); + + /** + * Used by a BlockAllocator to construct a block by setting the key values that a Block 'holds'. Most of the meaningful + * construction actually takes place within the BlockAllocator that calls this constructor. + * + * @param allocatorId Identifier of the BlockAllocator that owns the Block's memory resources. + * @param schema The schema of the data that can be read/written to the provided VectorSchema. + * @param vectorSchema Used to read/write values from the Apache Arrow memory buffers owned by this object. + */ + protected Block(String allocatorId, Schema schema, VectorSchemaRoot vectorSchema) + { + requireNonNull(allocatorId, "allocatorId is null"); + requireNonNull(schema, "schema is null"); + requireNonNull(vectorSchema, "vectorSchema is null"); + this.allocatorId = allocatorId; + this.schema = schema; + this.vectorSchema = vectorSchema; + } + + /** + * Used to constrain writes to the Block. + * + * @param constraintEvaluator The ConstraintEvaluator to use check if we should allow a value to be written to the Block. + * @note Setting the ConstraintEvaluator to null disables constraints. + */ + public void constrain(ConstraintEvaluator constraintEvaluator) + { + this.constraintEvaluator = (constraintEvaluator != null) ? constraintEvaluator : ConstraintEvaluator.emptyEvaluator(); + } + + /** + * Returns the ConstraintEvaluator used by the block. + */ + public ConstraintEvaluator getConstraintEvaluator() + { + return constraintEvaluator; + } + + public String getAllocatorId() + { + return allocatorId; + } + + public Schema getSchema() + { + return schema; + } + + /** + * Writes the provided value to the specified field on the specified row. This method does _not_ update the + * row count on the underlying Apache Arrow VectorSchema. You must call setRowCount(...) to ensure the values + * your have written are considered 'valid rows' and thus available when you attempt to serialize this Block. This + * method replies on BlockUtils' field conversion/coercion logic to convert the provided value into a type that + * matches Apache Arrow's supported serialization format. For more details on coercion please see @BlockUtils + * + * @param fieldName The name of the field you wish to write to. + * @param row The row number to write to. Note that Apache Arrow Blocks begin with row 0 just like a typical array. + * @param value The value you wish to write. + * @return True if the value was written to the Block, False if the value was not written due to failing a constraint. + * @note This method will throw an NPE if you call with with a non-existent field. You can use offerValue(...) + * to ignore non-existent fields. This can be useful when you are writing results and want to avoid checking + * if a field has been requested. One such example is when a query projects only a subset of columns and your + * underlying data store is not columnar. + */ + public boolean setValue(String fieldName, int row, Object value) + { + if (constraintEvaluator.apply(fieldName, value)) { + BlockUtils.setValue(getFieldVector(fieldName), row, value); + return true; + } + return false; + } + + /** + * Attempts to write the provided value to the specified field on the specified row. This method does _not_ update the + * row count on the underlying Apache Arrow VectorSchema. You must call setRowCount(...) to ensure the values + * your have written are considered 'valid rows' and thus available when you attempt to serialize this Block. This + * method replies on BlockUtils' field conversion/coercion logic to convert the provided value into a type that + * matches Apache Arrow's supported serialization format. For more details on coercion please see @BlockUtils + * + * @param fieldName The name of the field you wish to write to. + * @param row The row number to write to. Note that Apache Arrow Blocks begin with row 0 just like a typical array. + * @param value The value you wish to write. + * @return True if the value was written to the Block (even if the field is missing from the Block), + * False if the value was not written due to failing a constraint. + * @note This method will take no action if the provided fieldName is not a valid field in this Block's Schema. + * In such cases the method will return true. + */ + public boolean offerValue(String fieldName, int row, Object value) + { + if (constraintEvaluator.apply(fieldName, value)) { + FieldVector vector = getFieldVector(fieldName); + if (vector != null) { + BlockUtils.setValue(vector, row, value); + } + return true; + } + return false; + } + + /** + * Attempts to set the provided value for the given field name and row. If the Block's schema does not + * contain such a field, this method does nothing and returns false. + * + * @param fieldName The name of the field you wish to write to. + * @param row The row number to write to. Note that Apache Arrow Blocks begin with row 0 just like a typical array. + * @param value The value you wish to write. + * @return True if the value was written to the Block, False if the value was not written due to failing a constraint. + * @note This method will throw an NPE if you call with with a non-existent field. You can use offerComplexValue(...) + * to ignore non-existent fields. This can be useful when you are writing results and want to avoid checking + * if a field has been requested. One such example is when a query projects only a subset of columns and your + * underlying data store is not columnar. + */ + public boolean setComplexValue(String fieldName, int row, FieldResolver fieldResolver, Object value) + { + FieldVector vector = getFieldVector(fieldName); + BlockUtils.setComplexValue(vector, row, fieldResolver, value); + return true; + } + + /** + * Attempts to set the provided value for the given field name and row. If the Block's schema does not + * contain such a field, this method does nothing and returns false. + * + * @param fieldName The name of the field you wish to write to. + * @param row The row number to write to. Note that Apache Arrow Blocks begin with row 0 just like a typical array. + * @param value The value you wish to write. + * @return True if the value was written to the Block (even if the field is missing from the Block), + * False if the value was not written due to failing a constraint. + * @note This method will take no action if the provided fieldName is not a valid field in this Block's Schema. + * In such cases the method will return true. + */ + public boolean offerComplexValue(String fieldName, int row, FieldResolver fieldResolver, Object value) + { + FieldVector vector = getFieldVector(fieldName); + if (vector != null) { + BlockUtils.setComplexValue(vector, row, fieldResolver, value); + } + return true; + } + + /** + * Provides access to the Apache Arrow Vector Schema when direct access to Apache Arrow is required. + * + * @return The Apache Arrow Vector Schema. + */ + protected VectorSchemaRoot getVectorSchema() + { + return vectorSchema; + } + + /** + * Sets the valid row count on the underlying Apache Arrow Vector Schema. + * + * @param rowCount The row count to set. + * @Note If you do not set this value then block may not serialize correctly (too few rows) or rows may + * not be readable. + */ + public void setRowCount(int rowCount) + { + vectorSchema.setRowCount(rowCount); + } + + /** + * Returns the current row count as set by calling setRowCount(...) + * + * @return The current valud row count for the Apache Arrow Vector Schema. + */ + public int getRowCount() + { + return vectorSchema.getRowCount(); + } + + /** + * Provides access to the Apache Arrow FieldReader for the given field name. + * + * @param fieldName The name of the field to retrieve. + * @return The FieldReader that can be used to read values from the Block for the specified field. + * @note This method throws NPE if the requested field name is not a valid field name in the block's Schema. + * Additionally, for accessing nested field you must request the parent field and then call reader(String fieldName) + * on the parent FieldReader. You can find some examples of how to use Apache Arrow for complex/nested types in + * the UnitTest for this class or BlockUtils.java. + */ + public FieldReader getFieldReader(String fieldName) + { + return vectorSchema.getVector(fieldName).getReader(); + } + + /** + * Provides access to the Apache Arrow FieldVector which can be used to write values for the given field name. + * + * @param fieldName The name of the field to retrieve. + * @return The FieldVector that can be used to read values from the Block for the specified field or NULL if the field + * is not in this Block's Schema. + * @note Additionally, for accessing nested field you must request the parent field and then call the apprioriate + * method (based on type) to get the child field's FieldVector. You can find some examples of how to use Apache Arrow + * for complex/nested types in the UnitTest for this class or BlockUtils.java. + */ + public FieldVector getFieldVector(String fieldName) + { + return vectorSchema.getVector(fieldName); + } + + /** + * Provides access to the list of all top-level FieldReaders in this Block. + * + * @return List containing the top-level FieldReaders for this block. + */ + public List getFieldReaders() + { + List readers = new ArrayList<>(); + for (FieldVector next : vectorSchema.getFieldVectors()) { + readers.add(next.getReader()); + } + return readers; + } + + /** + * Calculates the current used size in 'bytes' for all Apache Arrow Buffers that comprise the row data for + * this Block. + * + * @return The used bytes of row data in this Block. + * @note This value is likley smaller than the actually memory held by this Block as it only counts the 'used' portion + * of the pre-allocated Apache Arrow Buffers. It is generally safer to think about this value as the size of the Block + * if you serialize it and thus is useful for controlling the size of the Block responses sent to Athena. + */ + @Transient + public long getSize() + { + long size = 0; + for (FieldVector next : vectorSchema.getFieldVectors()) { + size += next.getBufferSize(); + } + return size; + } + + /** + * Provides access to the list of all top-level FieldVectors in this Block. + * + * @return List containing the top-level FieldVectors for this block. + */ + public List getFieldVectors() + { + return vectorSchema.getFieldVectors(); + } + + /** + * Used to unload the Apache Arrow data in this Block in preparation for Serialization. + * + * @return An ArrowRecordBatch containing all row data in this Block for use in serializing the Block. + */ + public ArrowRecordBatch getRecordBatch() + { + VectorUnloader vectorUnloader = new VectorUnloader(vectorSchema); + return vectorUnloader.getRecordBatch(); + } + + /** + * Used to load Apache Arrow data into this Block after it has been deserialized. + * + * @param batch An ArrowRecordBatch containing all row data you'd like to load into this Block. + * @note The batch is closed after being loaded to avoid memory leaks or data corruption since the buffers + * associated with the batch are now owned by this Block. Closing the batch essentially decrements the referrence + * count in the Arrow Allocator. + */ + public void loadRecordBatch(ArrowRecordBatch batch) + { + VectorLoader vectorLoader = new VectorLoader(vectorSchema); + vectorLoader.load(batch); + batch.close(); + } + + /** + * Frees all Apache Arrow Buffers and resources associated with this block. + * + * @throws Exception + */ + @Override + public void close() + throws Exception + { + this.vectorSchema.close(); + } + + @Override + protected Schema internalGetSchema() + { + return schema; + } + + /** + * Provides some basic equality checking for a Block. This method has some draw backs in that is isn't a deep equality + * and will not work for some large complex blocks. At present this method is useful for testing purposes but may be refactored + * in a future release. + */ + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + Block that = (Block) o; + + if (this.schema.getFields().size() != that.schema.getFields().size()) { + return false; + } + + if (this.vectorSchema.getRowCount() != that.vectorSchema.getRowCount()) { + return false; + } + + try { + for (Field next : this.schema.getFields()) { + FieldReader thisReader = vectorSchema.getVector(next.getName()).getReader(); + FieldReader thatReader = that.vectorSchema.getVector(next.getName()).getReader(); + for (int i = 0; i < this.vectorSchema.getRowCount(); i++) { + thisReader.setPosition(i); + thatReader.setPosition(i); + if (ArrowTypeComparator.compare(thisReader, thisReader.readObject(), thatReader.readObject()) != 0) { + return false; + } + } + } + } + catch (IllegalArgumentException ex) { + //can happen when comparator doesn't support the type + throw ex; + } + catch (RuntimeException ex) { + //There are many differences which can cause an exception, easier to handle them this way + logger.warn("equals: ", ex); + return false; + } + + return true; + } + + /** + * Provides some basic equality checking for a Block ignoring ordering. This method has some draw backs in that is + * isn't a deep equality and will not work for some large complex blocks. At present this method is useful for testing + * purposes but may be refactored in a future release. + */ + public boolean equalsAsSet(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + Block that = (Block) o; + + if (this.schema.getFields().size() != that.schema.getFields().size()) { + return false; + } + + if (this.vectorSchema.getRowCount() != that.vectorSchema.getRowCount()) { + return false; + } + + try { + for (Field next : this.schema.getFields()) { + FieldReader thisReader = vectorSchema.getVector(next.getName()).getReader(); + FieldReader thatReader = that.vectorSchema.getVector(next.getName()).getReader(); + for (int i = 0; i < this.vectorSchema.getRowCount(); i++) { + thisReader.setPosition(i); + Types.MinorType type = thisReader.getMinorType(); + Object val = thisReader.readObject(); + boolean matched = false; + for (int j = 0; j < that.vectorSchema.getRowCount(); j++) { + thatReader.setPosition(j); + if (ArrowTypeComparator.compare(thatReader, val, thatReader.readObject()) == 0) { + matched = true; + } + } + if (!matched) { + return false; + } + } + } + } + catch (RuntimeException ex) { + //There are many differences which can cause an exception, easier to handle them this way + return false; + } + + return true; + } + + /** + * Provides some basic hashcode capabilities for the Block. This method has some draw backs in that it is difficult + * to maintain as we add new types and becomes error prone when and slow if missused. This challenge is compounded + * when understanding the right/wrong ways to use this are not easy to convey. + */ + @Override + public int hashCode() + { + int hashcode = 0; + for (Map.Entry next : this.schema.getCustomMetadata().entrySet()) { + hashcode = hashcode + Objects.hashCode(next); + } + + for (Field next : this.schema.getFields()) { + FieldReader thisReader = vectorSchema.getVector(next.getName()).getReader(); + for (int i = 0; i < this.vectorSchema.getRowCount(); i++) { + thisReader.setPosition(i); + hashcode = 31 * hashcode + Objects.hashCode(thisReader.readObject()); + } + } + return hashcode; + } + + @Override + public String toString() + { + MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(this); + helper.add("rows", getRowCount()); + + int rowsToPrint = this.vectorSchema.getRowCount() > 10 ? 10 : this.vectorSchema.getRowCount(); + for (Field next : this.schema.getFields()) { + FieldReader thisReader = vectorSchema.getVector(next.getName()).getReader(); + List values = new ArrayList<>(); + for (int i = 0; i < rowsToPrint; i++) { + thisReader.setPosition(i); + values.add(fieldToString(thisReader)); + } + helper.add(next.getName(), values); + } + + return helper.toString(); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/BlockAllocator.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/BlockAllocator.java new file mode 100644 index 0000000000..3d9ce27e68 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/BlockAllocator.java @@ -0,0 +1,109 @@ +package com.amazonaws.athena.connector.lambda.data; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import io.netty.buffer.ArrowBuf; +import org.apache.arrow.memory.BufferAllocator; +import org.apache.arrow.vector.ipc.message.ArrowRecordBatch; +import org.apache.arrow.vector.types.pojo.Schema; + +/** + * Defines the interface that should be implemented by all reference counting Apache Arrow resource allocators + * that are provided by this SDK. You should use a BlockAllocator over an Apache Arrow BufferAllocator if the lifecycle + * of your Apache Arrow resources are not fully contained in narrow code path. In practice we've found that ensuring + * proper lifecycle for Apache Arrow resources led us to change the structure of our code in ways that made it less + * maintainable than if we had a mechanism to control the lifecyle of Apache Arrow resources that cross-cut our + * request lifecycle. + */ +public interface BlockAllocator + extends AutoCloseable +{ + /** + * Creates an empty Apache Arrow Block with the provided Schema. + * + * @param schema The schema of the Apache Arrow Block. + * @return THe resulting Block. + * @note Once created the Block is also registered with this BlockAllocator such that closing this BlockAllocator + * also closes this Block, freeing its Apache Arrow resources. + */ + Block createBlock(Schema schema); + + /** + * Creates an empty Apache Arrow Buffer of the requested size. This is useful when working with certain Apache Arrow + * types directly. + * + * @param size The number of bytes to reserve for the requested buffer. + * @return THe resulting Apache Arrow Buffer.. + * @note Once created the buffer is also registered with this BlockAllocator such that closing this BlockAllocator + * also closes this buffer, freeing its Apache Arrow resources. + */ + ArrowBuf createBuffer(int size); + + /** + * Allows for a leak-free way to create Apache Arrow Batches. At first glance this method's signature may seem ackward + * when compared to createBuffer(...) or createBlock(...) but ArrowRecordBatches are typically as part of serialization + * and as such are prone to leakage when you serialize or deserialize and invalid Block. With this approach the + * BlockAllocator is able to capture any exceptions from your BatchGenerator and perform nessesary clean up without + * your code having to implement the boiler plate for handling those edge cases. + * + * @param generator The generator which is expected to create an ArrowRecordBatch. + * @return THe resulting Apache Arrow Batch.. + * @note Once created the batch is also registered with this BlockAllocator such that closing this BlockAllocator + * also closes this batch, freeing its Apache Arrow resources. + */ + ArrowRecordBatch registerBatch(BatchGenerator generator); + + /** + * Provides access to the current memory pool usage on the underlying Apache Arrow BufferAllocator. + * + * @return The number of bytes that have been used (e.g. assigned to an Apache Arrow Resource like a block, batch, or buffer). + */ + long getUsage(); + + /** + * Closes all Apache Arrow resources tracked by this BlockAllocator, freeing their memory. + */ + void close(); + + /** + * Provides access to the current state of this BlockAllocator. + * + * @return True if close has been called, False otherwise. + */ + boolean isClosed(); + + /** + * Used to generate a batch in a leak free way using the BlockAllocator to handle + * the boiler plate aspects of error detection and rollback. + */ + interface BatchGenerator + { + /** + * When called by the BlockAllocator you can generate your batch. + * + * @param allocator A referrence to the BlockAllocator you can use to create your batch. + * @return The resulting ArrowRecordBatch. + * @throws Exception + */ + ArrowRecordBatch generate(BufferAllocator allocator) + throws Exception; + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/BlockAllocatorImpl.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/BlockAllocatorImpl.java new file mode 100644 index 0000000000..5bcece0b93 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/BlockAllocatorImpl.java @@ -0,0 +1,287 @@ +package com.amazonaws.athena.connector.lambda.data; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import io.netty.buffer.ArrowBuf; +import org.apache.arrow.memory.BufferAllocator; +import org.apache.arrow.memory.RootAllocator; +import org.apache.arrow.util.VisibleForTesting; +import org.apache.arrow.vector.FieldVector; +import org.apache.arrow.vector.VectorSchemaRoot; +import org.apache.arrow.vector.ipc.message.ArrowRecordBatch; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.arrow.vector.types.pojo.Schema; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Basic BlockAllocator which uses reference counting to perform garbage collection of Apache Arrow resources. + * + * @see com.amazonaws.athena.connector.lambda.data.BlockAllocator + */ +public class BlockAllocatorImpl + implements BlockAllocator +{ + private static final Logger logger = LoggerFactory.getLogger(BlockAllocatorImpl.class); + + //Identifier for this block allocator, mostly used by BlockAllocatorRegistry. + private final String id; + //The Apache Arrow Buffer Allocator that we are wrapping with reference counting and clean up. + private final BufferAllocator rootAllocator; + //The Blocks that have been allocated via this BlockAllocator + private final List blocks = new ArrayList<>(); + //The record batches that have been allocated via this BlockAllocator + private final List recordBatches = new ArrayList<>(); + //The arrow buffers that have been allocated via this BlockAllocator + private final List arrowBufs = new ArrayList<>(); + //Flag inficating if this allocator has been closed. + private final AtomicBoolean isClosed = new AtomicBoolean(false); + + /** + * Default constructor. + */ + public BlockAllocatorImpl() + { + this(UUID.randomUUID().toString(), Integer.MAX_VALUE); + } + + /** + * Constructs a BlockAllocatorImpl with the given id. + * + * @param id The id used to identify this BlockAllocatorImpl + */ + public BlockAllocatorImpl(String id) + { + this(id, Integer.MAX_VALUE); + } + + /** + * Constructs a BlockAllocatorImpl with the given id and memory byte limit. + * + * @param id The id used to identify this BlockAllocatorImpl + * @param memoryLimit The max memory, in bytes, that this BlockAllocator is allows to use. + */ + public BlockAllocatorImpl(String id, long memoryLimit) + { + this.rootAllocator = new RootAllocator(memoryLimit); + this.id = id; + } + + /** + * Creates a block and registers it for later clean up if the block isn't explicitly closed by the caller. + * + * @see com.amazonaws.athena.connector.lambda.data.BlockAllocator + */ + public synchronized Block createBlock(Schema schema) + { + Block block = null; + VectorSchemaRoot vectorSchemaRoot = null; + List vectors = new ArrayList(); + try { + for (Field next : schema.getFields()) { + vectors.add(next.createVector(rootAllocator)); + } + vectorSchemaRoot = new VectorSchemaRoot(schema, vectors, 0); + block = new Block(id, schema, vectorSchemaRoot); + blocks.add(block); + } + catch (Exception ex) { + if (block != null) { + try { + block.close(); + } + catch (Exception ex2) { + logger.error("createBlock: error while closing block during previous error.", ex2); + } + } + + if (vectorSchemaRoot != null) { + vectorSchemaRoot.close(); + } + + for (FieldVector next : vectors) { + next.close(); + } + + throw ex; + } + return block; + } + + /** + * Creates an ArrowBuf and registers it for later clean up if the ArrowBuff isn't explicitly closed by the caller. + * + * @see com.amazonaws.athena.connector.lambda.data.BlockAllocator + */ + public ArrowBuf createBuffer(int size) + { + ArrowBuf buffer = null; + try { + buffer = rootAllocator.buffer(size); + arrowBufs.add(buffer); + return buffer; + } + catch (Exception ex) { + if (buffer != null) { + buffer.close(); + } + throw ex; + } + } + + /** + * Creates an ArrowRecordBatch and registers it for later clean up if the ArrowRecordBatch isn't explicitly closed + * by the caller. + * + * @see com.amazonaws.athena.connector.lambda.data.BlockAllocator + */ + public synchronized ArrowRecordBatch registerBatch(BatchGenerator generator) + { + try { + logger.debug("registerBatch: {}", recordBatches.size()); + ArrowRecordBatch batch = generator.generate(getRawAllocator()); + recordBatches.add(batch); + return batch; + } + catch (org.apache.arrow.memory.OutOfMemoryException ex) { + //Must not wrap or we may break resource management logic elsewhere + throw ex; + } + catch (RuntimeException ex) { + throw ex; + } + catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + /** + * Provides access to the underlying Apache Arrow Allocator. + * + * @see com.amazonaws.athena.connector.lambda.data.BlockAllocator + */ + protected synchronized BufferAllocator getRawAllocator() + { + logger.debug("getRawAllocator: enter"); + return rootAllocator; + } + + /** + * Attempts to close all Blocks allocated by this BlockAllocator. + */ + @VisibleForTesting + protected synchronized void closeBlocks() + { + logger.debug("closeBlocks: {}", blocks.size()); + for (Block next : blocks) { + try { + next.close(); + } + catch (Exception ex) { + logger.warn("closeBlocks: Error closing block", ex); + } + } + blocks.clear(); + } + + /** + * Attempts to close all buffers allocated by this BlockAllocator. + */ + @VisibleForTesting + protected synchronized void closeBuffers() + { + logger.debug("closeBuffers: {}", arrowBufs.size()); + for (ArrowBuf next : arrowBufs) { + try { + next.close(); + } + catch (Exception ex) { + logger.warn("closeBuffers: Error closing buffer", ex); + } + } + arrowBufs.clear(); + } + + /** + * Attempts to close all batches allocated by this BlockAllocator. + */ + @VisibleForTesting + protected synchronized void closeBatches() + { + logger.debug("closeBatches: {}", recordBatches.size()); + for (ArrowRecordBatch next : recordBatches) { + try { + next.close(); + } + catch (Exception ex) { + logger.warn("closeBatches: Error closing batch", ex); + } + } + recordBatches.clear(); + } + + /** + * Returns number of bytes in the Apache Arrow Pool that are used. This is not the same as the actual + * reserved memory usage you may be familiar with from your operating system. + * + * @see com.amazonaws.athena.connector.lambda.data.BlockAllocator + */ + public long getUsage() + { + return rootAllocator.getAllocatedMemory(); + } + + /** + * Closes all Apache Arrow Resources allocated via this BlockAllocator and then attempts to + * close the underlying Apache Arrow Allocator which would actually free memory. This operation may + * fail if the underlying Apache Arrow Allocator was used to allocate resources without registering + * them to this BlockAllocator and those resources were not freed prior to calling close. + * + * @see com.amazonaws.athena.connector.lambda.data.BlockAllocator + */ + @Override + public synchronized void close() + { + if (!isClosed.get()) { + isClosed.set(true); + closeBatches(); + closeBlocks(); + closeBuffers(); + rootAllocator.close(); + } + } + + /** + * Indicates if this BlockAllocator has been closed. + * + * @see com.amazonaws.athena.connector.lambda.data.BlockAllocator + */ + @Override + public boolean isClosed() + { + return isClosed.get(); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/BlockAllocatorRegistry.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/BlockAllocatorRegistry.java new file mode 100644 index 0000000000..7eede65e9c --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/BlockAllocatorRegistry.java @@ -0,0 +1,41 @@ +package com.amazonaws.athena.connector.lambda.data; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +/** + * Used to track BlockAllocators in transactional environments where you want tighter control over + * how much memory a particular transaction uses. By using the same BlockAllocator for all resources + * in a given transaction you can limit the total memory used by that transaction. This also proves + * to be a useful mechanism to inject BlockAllocators in contexts which are difficult to access with + * more traditional dependency injection mechanism. One such example is in a ObjectMapper that is deserializing + * and incoming request. + */ +public interface BlockAllocatorRegistry +{ + /** + * Gets or creates a new Block Allocator for the given context (id). + * + * @param id The id of the context for which you'd like a BlockAllocator. + * @return The BlockAllocator associated with that id, or a new BlockAllocator for that id which will then + * be vended for any future calls for that id. + */ + BlockAllocator getOrCreateAllocator(String id); +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/BlockSpiller.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/BlockSpiller.java new file mode 100644 index 0000000000..535a867a56 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/BlockSpiller.java @@ -0,0 +1,64 @@ +package com.amazonaws.athena.connector.lambda.data; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.domain.predicate.ConstraintEvaluator; +import com.amazonaws.athena.connector.lambda.domain.spill.SpillLocation; + +import java.util.List; + +/** + * Used to write blocks which may require chunking and optionally spilling via a secondary communication channel. + */ +public interface BlockSpiller + extends BlockWriter +{ + /** + * Indicates if any part of the response written thus far has been spilled. + * + * @return True if at least 1 block has spilled, False otherwise. + */ + boolean spilled(); + + /** + * Provides access to the single buffered block in the event that spilled() is false. + * + * @return + */ + Block getBlock(); + + /** + * Provides access to the manifest of SpillLocation(s) if spilled is true. + * + * @return + */ + List getSpillLocations(); + + /** + * Frees any resources associated with the BlockSpiller. + */ + void close(); + + /** + * Provides access to the ConstraintEvaluator that will be applied to the generated Blocks. + */ + ConstraintEvaluator getConstraintEvaluator(); +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/BlockUtils.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/BlockUtils.java new file mode 100644 index 0000000000..6dd9b16e4a --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/BlockUtils.java @@ -0,0 +1,1108 @@ +package com.amazonaws.athena.connector.lambda.data; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import io.netty.buffer.ArrowBuf; +import org.apache.arrow.memory.BufferAllocator; +import org.apache.arrow.util.VisibleForTesting; +import org.apache.arrow.vector.BigIntVector; +import org.apache.arrow.vector.BitVector; +import org.apache.arrow.vector.DateDayVector; +import org.apache.arrow.vector.DateMilliVector; +import org.apache.arrow.vector.DecimalVector; +import org.apache.arrow.vector.FieldVector; +import org.apache.arrow.vector.Float4Vector; +import org.apache.arrow.vector.Float8Vector; +import org.apache.arrow.vector.IntVector; +import org.apache.arrow.vector.SmallIntVector; +import org.apache.arrow.vector.TinyIntVector; +import org.apache.arrow.vector.UInt1Vector; +import org.apache.arrow.vector.UInt2Vector; +import org.apache.arrow.vector.UInt4Vector; +import org.apache.arrow.vector.UInt8Vector; +import org.apache.arrow.vector.VarBinaryVector; +import org.apache.arrow.vector.VarCharVector; +import org.apache.arrow.vector.complex.ListVector; +import org.apache.arrow.vector.complex.StructVector; +import org.apache.arrow.vector.complex.impl.UnionListWriter; +import org.apache.arrow.vector.complex.reader.FieldReader; +import org.apache.arrow.vector.complex.writer.BaseWriter.StructWriter; +import org.apache.arrow.vector.complex.writer.FieldWriter; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.arrow.vector.types.pojo.Schema; +import org.apache.arrow.vector.util.Text; +import org.apache.commons.codec.Charsets; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.Arrays; +import java.util.Collection; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * This utility class abstracts many facets of reading and writing values into Apache Arrow's FieldReader and FieldVector + * objects. + * + * @note This class encourages a row wise approach to writing results. These interfaces are often viewed as simpler than + * the would be columnar equivalents. Even though many of the systems that we've integrated with using this SDK do not + * themselves support columnar access patterns, there is value in offering a a variant of these mechanisms that provide + * the skeleton for columnar writing/reading of results. + *

+ * The current SDK version takes the approach that experts can drop into 'native' Apache Arrow mode and simply not use + * this abstraction. This approach of making common things easy and still enabling access to a 'power user' mode is + * one we'd like to stick with but we'd also like to make it easier for customers that can/want a more columnar + * experience/performance to be able to do so more easily. + *

+ * In general the abstractions provided by this utility class also come with a performance hit when compared with native, + * columnar, Apache Arrow access patterns. The performance overhead primarily results from Object overhead related to boxing + * and/or type conversion. The second source of overhead is the constant lookup and branching of field types, vectors, readers, + * etc.. Some of this second category of overhead can be mitigated by being mindful of how you use this class but a more + * ideal solution would be to offer an interface that steers you in a better direction. + *

+ * An issue has been opened to track the creation of a columnar variant of this utility: + * https://github.com/awslabs/aws-athena-query-federation/issues/1 + */ +public class BlockUtils +{ + public static final ZoneId UTC_ZONE_ID = ZoneId.of("UTC"); + + /** + * Creates a new Block with a single column and populated with the provided values. + * + * @param allocator The BlockAllocator to use when creating the Block. + * @param columnName The name of the single column in the Block's Schema. + * @param type The Apache Arrow Type of the column. + * @param values The values to write to the new Block. Each value will be its own row. + * @return The newly created Block with a single column Schema at populated with the provided values. + */ + public static Block newBlock(BlockAllocator allocator, String columnName, ArrowType type, Object... values) + { + return newBlock(allocator, columnName, type, Arrays.asList(values)); + } + + /** + * Creates a new Block with a single column and populated with the provided values. + * + * @param allocator The BlockAllocator to use when creating the Block. + * @param columnName The name of the single column in the Block's Schema. + * @param type The Apache Arrow Type of the column. + * @param values The values to write to the new Block. Each value will be its own row. + * @return The newly created Block with a single column Schema at populated with the provided values. + */ + public static Block newBlock(BlockAllocator allocator, String columnName, ArrowType type, Collection values) + { + SchemaBuilder schemaBuilder = new SchemaBuilder(); + schemaBuilder.addField(columnName, type); + Schema schema = schemaBuilder.build(); + Block block = allocator.createBlock(schema); + int count = 0; + for (Object next : values) { + try { + setValue(block.getFieldVector(columnName), count++, next); + } + catch (Exception ex) { + throw new RuntimeException("Error for " + type + " " + columnName + " " + next, ex); + } + } + block.setRowCount(count); + return block; + } + + /** + * Creates a new, empty, Block with a single column. + * + * @param allocator The BlockAllocator to use when creating the Block. + * @param columnName The name of the single column in the Block's Schema. + * @param type The Apache Arrow Type of the column. + * @return The newly created, empty, Block with a single column Schema. + */ + public static Block newEmptyBlock(BlockAllocator allocator, String columnName, ArrowType type) + { + SchemaBuilder schemaBuilder = new SchemaBuilder(); + schemaBuilder.addField(columnName, type); + Schema schema = schemaBuilder.build(); + return allocator.createBlock(schema); + } + + /** + * Used to set complex values (Struct, List, etc...) on the provided FieldVector. + * + * @param vector The FieldVector into which we should write the provided value. + * @param pos The row number that the value should be written to. + * @param resolver The FieldResolver that can be used to map your value to the complex type (mostly for Structs, Maps). + * @param value The value to write. + * @note This method incurs more Object overhead (heap churn) than using Arrow's native interface. Users of this Utility + * should weigh their performance needs vs. the readability / ease of use. + */ + public static void setComplexValue(FieldVector vector, int pos, FieldResolver resolver, Object value) + { + if (vector instanceof ListVector) { + if (value != null) { + UnionListWriter writer = ((ListVector) vector).getWriter(); + writer.setPosition(pos); + writeList(vector.getAllocator(), + writer, + vector.getField(), + pos, + ((List) value).iterator(), + resolver); + ((ListVector) vector).setNotNull(pos); + } + } + else if (vector instanceof StructVector) { + StructWriter writer = ((StructVector) vector).getWriter(); + writer.setPosition(pos); + writeStruct(vector.getAllocator(), + writer, + vector.getField(), + pos, + value, + resolver); + } + else { + throw new RuntimeException("Unsupported 'Complex' vector " + + vector.getClass().getSimpleName() + " for field " + vector.getField().getName()); + } + } + + /** + * Used to set values (Int, BigInt, Bit, etc...) on the provided FieldVector. + * + * @param vector The FieldVector into which we should write the provided value. + * @param pos The row number that the value should be written to. + * @param value The value to write. + * @note This method incurs more Object overhead (heap churn) than using Arrow's native interface. Users of this Utility + * should weigh their performance needs vs. the readability / ease of use. + */ + public static void setValue(FieldVector vector, int pos, Object value) + { + try { + if (value == null) { + setNullValue(vector, pos); + return; + } + + //TODO: add all types + switch (vector.getMinorType()) { + case DATEMILLI: + if (value instanceof Date) { + ((DateMilliVector) vector).setSafe(pos, ((Date) value).getTime()); + } + else if (value instanceof LocalDateTime) { + ((DateMilliVector) vector).setSafe( + pos, + ((LocalDateTime) value).atZone(UTC_ZONE_ID).toInstant().toEpochMilli()); + } + else { + ((DateMilliVector) vector).setSafe(pos, (long) value); + } + break; + case DATEDAY: + if (value instanceof Date) { + org.joda.time.Days days = org.joda.time.Days.daysBetween(EPOCH, + new org.joda.time.DateTime(((Date) value).getTime())); + ((DateDayVector) vector).setSafe(pos, days.getDays()); + } + if (value instanceof LocalDate) { + int days = (int) ((LocalDate) value).toEpochDay(); + ((DateDayVector) vector).setSafe(pos, days); + } + else if (value instanceof Long) { + ((DateDayVector) vector).setSafe(pos, ((Long) value).intValue()); + } + else { + ((DateDayVector) vector).setSafe(pos, (int) value); + } + break; + case FLOAT8: + ((Float8Vector) vector).setSafe(pos, (double) value); + break; + case FLOAT4: + ((Float4Vector) vector).setSafe(pos, (float) value); + break; + case INT: + if (value != null && value instanceof Long) { + //This may seem odd at first but many frameworks (like Presto) use long as the preferred + //native java type for representing integers. We do this to keep type conversions simple. + ((IntVector) vector).setSafe(pos, ((Long) value).intValue()); + } + else { + ((IntVector) vector).setSafe(pos, (int) value); + } + break; + case TINYINT: + if (value instanceof Byte) { + ((TinyIntVector) vector).setSafe(pos, (byte) value); + } + else { + ((TinyIntVector) vector).setSafe(pos, (int) value); + } + break; + case SMALLINT: + if (value instanceof Short) { + ((SmallIntVector) vector).setSafe(pos, (short) value); + } + else { + ((SmallIntVector) vector).setSafe(pos, (int) value); + } + break; + case UINT1: + ((UInt1Vector) vector).setSafe(pos, (int) value); + break; + case UINT2: + ((UInt2Vector) vector).setSafe(pos, (int) value); + break; + case UINT4: + ((UInt4Vector) vector).setSafe(pos, (int) value); + break; + case UINT8: + ((UInt8Vector) vector).setSafe(pos, (int) value); + break; + case BIGINT: + ((BigIntVector) vector).setSafe(pos, (long) value); + break; + case VARBINARY: + ((VarBinaryVector) vector).setSafe(pos, (byte[]) value); + break; + case DECIMAL: + DecimalVector dVector = ((DecimalVector) vector); + if (value instanceof Double) { + BigDecimal bdVal = new BigDecimal((double) value); + bdVal = bdVal.setScale(dVector.getScale(), RoundingMode.HALF_UP); + dVector.setSafe(pos, bdVal); + } + else { + BigDecimal scaledValue = ((BigDecimal) value).setScale(dVector.getScale(), RoundingMode.HALF_UP); + ((DecimalVector) vector).setSafe(pos, scaledValue); + } + break; + case VARCHAR: + if (value instanceof String) { + ((VarCharVector) vector).setSafe(pos, ((String) value).getBytes(Charsets.UTF_8)); + } + else { + ((VarCharVector) vector).setSafe(pos, (Text) value); + } + break; + case BIT: + if (value instanceof Integer && (int) value > 0) { + ((BitVector) vector).setSafe(pos, 1); + } + else if (value instanceof Boolean && (boolean) value) { + ((BitVector) vector).setSafe(pos, 1); + } + else { + ((BitVector) vector).setSafe(pos, 0); + } + break; + default: + throw new IllegalArgumentException("Unknown type " + vector.getMinorType()); + } + } + catch (RuntimeException ex) { + String fieldName = (vector != null) ? vector.getField().getName() : "null_vector"; + throw new RuntimeException("Unable to set value for field " + fieldName + " using value " + value, ex); + } + } + + /** + * Used to convert a specific row in the provided Block to a human readable string. This is useful for diagnostic + * logging. + * + * @param block The Block to read the row from. + * @param row The row number to read. + * @return The human readable String representation of the requested row. + */ + public static String rowToString(Block block, int row) + { + if (row > block.getRowCount()) { + throw new IllegalArgumentException(row + " exceeds available rows " + block.getRowCount()); + } + + StringBuilder sb = new StringBuilder(); + for (FieldReader nextReader : block.getFieldReaders()) { + try { + nextReader.setPosition(row); + if (sb.length() > 0) { + sb.append(", "); + } + sb.append("["); + sb.append(nextReader.getField().getName()); + sb.append(" : "); + sb.append(fieldToString(nextReader)); + sb.append("]"); + } + catch (RuntimeException ex) { + throw new RuntimeException("Error processing field " + nextReader.getField().getName(), ex); + } + } + + return sb.toString(); + } + + /** + * Used to convert a single cell for the given FieldReader to a human readable string. + * + * @param reader The FieldReader from which we should read the current cell. This means the position to be read should + * have been set on the reader before calling this method. + * @return The human readable String representation of the value at the FieldReaders current position. + */ + public static String fieldToString(FieldReader reader) + { + switch (reader.getMinorType()) { + case DATEDAY: + return String.valueOf(reader.readInteger()); + case DATEMILLI: + return String.valueOf(reader.readLocalDateTime()); + case FLOAT8: + case FLOAT4: + case UINT4: + case UINT8: + case INT: + case BIGINT: + case VARCHAR: + case BIT: + return String.valueOf(reader.readObject()); + case DECIMAL: + return String.valueOf(reader.readBigDecimal()); + case SMALLINT: + return String.valueOf(reader.readShort()); + case TINYINT: + case UINT1: + return Integer.valueOf(reader.readByte()).toString(); + case UINT2: + return Integer.valueOf(reader.readCharacter()).toString(); + case VARBINARY: + return bytesToHex(reader.readByteArray()); + case STRUCT: + StringBuilder sb = new StringBuilder(); + sb.append("{"); + for (Field child : reader.getField().getChildren()) { + if (sb.length() > 3) { + sb.append(","); + } + sb.append("["); + sb.append(child.getName()); + sb.append(" : "); + sb.append(fieldToString(reader.reader(child.getName()))); + sb.append("]"); + } + sb.append("}"); + return sb.toString(); + case LIST: + StringBuilder sbList = new StringBuilder(); + sbList.append("{"); + while (reader.next()) { + if (sbList.length() > 1) { + sbList.append(","); + } + sbList.append(fieldToString(reader.reader())); + } + sbList.append("}"); + return sbList.toString(); + default: + Object obj = reader.readObject(); + return reader.getMinorType() + " - " + ((obj != null) ? obj.getClass().toString() : "null") + + "[ " + String.valueOf(obj) + " ]"; + } + } + + /** + * Copies a inclusive range of rows from one block to another. + * + * @param srcBlock The source Block to copy the range of rows from. + * @param dstBlock The destination Block to copy the range of rows to. + * @param firstRow The first row we'd like to copy. + * @param lastRow The last row we'd like to copy. + * @return The number of rows that were copied. + */ + public static int copyRows(Block srcBlock, Block dstBlock, int firstRow, int lastRow) + { + if (firstRow > lastRow || lastRow > srcBlock.getRowCount() - 1) { + throw new RuntimeException("src has " + srcBlock.getRowCount() + + " but requested copy of " + firstRow + " to " + lastRow); + } + + for (FieldReader src : srcBlock.getFieldReaders()) { + int dstOffset = dstBlock.getRowCount(); + for (int i = firstRow; i <= lastRow; i++) { + FieldVector dst = dstBlock.getFieldVector(src.getField().getName()); + src.setPosition(i); + setValue(dst, dstOffset++, src.readObject()); + } + } + + int rowsCopied = 1 + (lastRow - firstRow); + dstBlock.setRowCount(dstBlock.getRowCount() + rowsCopied); + return rowsCopied; + } + + /** + * Checks if a row is null by checking that all fields in that row are null (aka not set). + * + * @param block The Block we'd like to check. + * @param row The row number we'd like to check. + * @return True if the entire row is null (aka all fields null/unset), False if any field has a non-null value. + */ + public static boolean isNullRow(Block block, int row) + { + if (row > block.getRowCount() - 1) { + throw new RuntimeException("block has " + block.getRowCount() + + " rows but requested to check " + row); + } + + //If any column is non-null then return false + for (FieldReader src : block.getFieldReaders()) { + src.setPosition(row); + if (src.isSet()) { + return false; + } + } + + return true; + } + + /** + * Used to write a List value. + * + * @param allocator The BlockAllocator which can be used to generate Apache Arrow Buffers for types + * which require conversion to an Arrow Buffer before they can be written using the FieldWriter. + * @param writer The FieldWriter for the List field we'd like to write into. + * @param field The Schema details of the List Field we are writing into. + * @param pos The position (row) in the Apache Arrow batch we are writing to. + * @param value An iterator to the collection of values we want to write into the row. + * @param resolver The field resolver that can be used to extract individual values from the value iterator. + */ + @VisibleForTesting + protected static void writeList(BufferAllocator allocator, + FieldWriter writer, + Field field, + int pos, + Iterator value, + FieldResolver resolver) + { + //Apache Arrow List types have a single 'special' child field which gives us the concrete type of the values + //stored in the list. + Field child = null; + if (field.getChildren() != null && !field.getChildren().isEmpty()) { + child = field.getChildren().get(0); + } + + //Mark the beginning of the list, this is essentially how Apache Arrow handles the variable length nature + //of lists. + writer.startList(); + + Iterator itr = value; + while (itr.hasNext()) { + //For each item in the iterator, attempt to write it to the list. + Object val = itr.next(); + if (val != null) { + switch (Types.getMinorTypeForArrowType(child.getType())) { + case LIST: + try { + writeList(allocator, (FieldWriter) writer.list(), child, pos, ((List) val).iterator(), resolver); + } + catch (Exception ex) { + throw ex; + } + break; + case STRUCT: + writeStruct(allocator, writer.struct(), child, pos, val, resolver); + break; + default: + writeListValue(writer, child.getType(), allocator, val); + break; + } + } + } + writer.endList(); + } + + /** + * Used to write a Struct value. + * + * @param allocator The BlockAllocator which can be used to generate Apache Arrow Buffers for types + * which require conversion to an Arrow Buffer before they can be written using the FieldWriter. + * @param writer The FieldWriter for the Struct field we'd like to write into. + * @param field The Schema details of the Struct Field we are writing into. + * @param pos The position (row) in the Apache Arrow batch we are writing to. + * @param value The value we'd like to write as a struct. + * @param resolver The field resolver that can be used to extract individual Struct fields from the value. + */ + @VisibleForTesting + protected static void writeStruct(BufferAllocator allocator, + StructWriter writer, + Field field, + int pos, + Object value, + FieldResolver resolver) + { + //We expect null writes to have been handled earlier so this is a no-op. + if (value == null) { + return; + } + + //Indicate the beginning of the struct value, this is how Apache Arrow handles the variable length of Struct types. + writer.start(); + for (Field nextChild : field.getChildren()) { + //For each child field that comprises the struct, attempt to extract and write the corresponding value + //using the FieldResolver. + Object childValue = resolver.getFieldValue(nextChild, value); + switch (Types.getMinorTypeForArrowType(nextChild.getType())) { + case LIST: + writeList(allocator, + (FieldWriter) writer.list(nextChild.getName()), + nextChild, + pos, + ((List) childValue).iterator(), + resolver); + break; + case STRUCT: + writeStruct(allocator, + writer.struct(nextChild.getName()), + nextChild, + pos, + childValue, + resolver); + break; + default: + writeStructValue(writer, nextChild, allocator, childValue); + break; + } + } + writer.end(); + } + + @VisibleForTesting + /** + * Maps an Arrow Type to a Java class. + * @param minorType + * @return Java class mapping the Arrow type + */ + public static Class getJavaType(Types.MinorType minorType) + { + switch (minorType) { + case DATEMILLI: + return LocalDateTime.class; + case TINYINT: + case UINT1: + return Byte.class; + case SMALLINT: + return Short.class; + case UINT2: + return Character.class; + case DATEDAY: + return LocalDate.class; + case INT: + case UINT4: + return Integer.class; + case UINT8: + case BIGINT: + return Long.class; + case DECIMAL: + return BigDecimal.class; + case FLOAT4: + return Float.class; + case FLOAT8: + return Double.class; + case VARCHAR: + return String.class; + case VARBINARY: + return byte[].class; + case BIT: + return Boolean.class; + case LIST: + return List.class; + case STRUCT: + return Map.class; + default: + throw new IllegalArgumentException("Unknown type " + minorType); + } + } + + /** + * Used to write an individual value into a List field, multiple calls to this method per-cell are expected in order + * to write the N values of a list of size N. + * + * @param writer The FieldWriter (already positioned at the row and list entry number) that we want to write into. + * @param type The concrete type of the List's values. + * @param allocator The BlockAllocator that can be used for allocating Arrow Buffers for fields which require conversion + * to Arrow Buff before being written. + * @param value The value to write. + * @note This method and its Struct complement violate the DRY mantra because ListWriter and StructWriter don't share + * a meaningful ancestor despite having identical methods. This requires us to either further wrap and abstract the writer + * or duplicate come code. In a future release we hope to have contributed a better option to Apache Arrow which allows + * us to simplify this method. + */ + protected static void writeListValue(FieldWriter writer, ArrowType type, BufferAllocator allocator, Object value) + { + try { + //TODO: add all types + switch (Types.getMinorTypeForArrowType(type)) { + case DATEMILLI: + if (value instanceof Date) { + writer.writeDateMilli(((Date) value).getTime()); + } + else { + writer.writeDateMilli((long) value); + } + break; + case DATEDAY: + if (value instanceof Date) { + org.joda.time.Days days = org.joda.time.Days.daysBetween(EPOCH, + new org.joda.time.DateTime(((Date) value).getTime())); + writer.writeDateDay(days.getDays()); + } + else if (value instanceof LocalDate) { + int days = (int) ((LocalDate) value).toEpochDay(); + writer.writeDateDay(days); + } + else if (value instanceof Long) { + writer.writeDateDay(((Long) value).intValue()); + } + else { + writer.writeDateDay((int) value); + } + break; + case FLOAT8: + writer.float8().writeFloat8((double) value); + break; + case FLOAT4: + writer.float4().writeFloat4((float) value); + break; + case INT: + if (value != null && value instanceof Long) { + //This may seem odd at first but many frameworks (like Presto) use long as the preferred + //native java type for representing integers. We do this to keep type conversions simple. + writer.integer().writeInt(((Long) value).intValue()); + } + else { + writer.integer().writeInt((int) value); + } + break; + case TINYINT: + writer.tinyInt().writeTinyInt((byte) value); + break; + case SMALLINT: + writer.smallInt().writeSmallInt((short) value); + break; + case UINT1: + writer.uInt1().writeUInt1((byte) value); + break; + case UINT2: + writer.uInt2().writeUInt2((char) value); + break; + case UINT4: + writer.uInt4().writeUInt4((int) value); + break; + case UINT8: + writer.uInt8().writeUInt8((long) value); + break; + case BIGINT: + writer.bigInt().writeBigInt((long) value); + break; + case VARBINARY: + if (value instanceof ArrowBuf) { + ArrowBuf buf = (ArrowBuf) value; + writer.varBinary().writeVarBinary(0, buf.capacity(), buf); + } + else if (value instanceof byte[]) { + byte[] bytes = (byte[]) value; + try (ArrowBuf buf = allocator.buffer(bytes.length)) { + buf.writeBytes(bytes); + writer.varBinary().writeVarBinary(0, buf.readableBytes(), buf); + } + } + break; + case DECIMAL: + int scale = ((ArrowType.Decimal) type).getScale(); + if (value instanceof Double) { + int precision = ((ArrowType.Decimal) type).getPrecision(); + BigDecimal bdVal = new BigDecimal((double) value); + bdVal = bdVal.setScale(scale, RoundingMode.HALF_UP); + writer.decimal().writeDecimal(bdVal); + } + else { + BigDecimal scaledValue = ((BigDecimal) value).setScale(scale, RoundingMode.HALF_UP); + writer.decimal().writeDecimal(scaledValue); + } + break; + case VARCHAR: + if (value instanceof String) { + byte[] bytes = ((String) value).getBytes(Charsets.UTF_8); + try (ArrowBuf buf = allocator.buffer(bytes.length)) { + buf.writeBytes(bytes); + writer.varChar().writeVarChar(0, buf.readableBytes(), buf); + } + } + else if (value instanceof ArrowBuf) { + ArrowBuf buf = (ArrowBuf) value; + writer.varChar().writeVarChar(0, buf.readableBytes(), buf); + } + else if (value instanceof byte[]) { + byte[] bytes = (byte[]) value; + try (ArrowBuf buf = allocator.buffer(bytes.length)) { + buf.writeBytes(bytes); + writer.varChar().writeVarChar(0, buf.readableBytes(), buf); + } + } + break; + case BIT: + if (value instanceof Integer && (int) value > 0) { + writer.bit().writeBit(1); + } + else if (value instanceof Boolean && (boolean) value) { + writer.bit().writeBit(1); + } + else { + writer.bit().writeBit(0); + } + break; + default: + throw new IllegalArgumentException("Unknown type " + type); + } + } + catch (RuntimeException ex) { + String fieldName = (writer.getField() != null) ? writer.getField().getName() : "null_vector"; + throw new RuntimeException("Unable to write value for field " + fieldName + " using value " + value, ex); + } + } + + /** + * Used to write a value into a specific child field within a Struct. Multiple calls to this method per-cell are + * expected in order to write to all N fields of a Struct. + * + * @param writer The FieldWriter (already positioned at the row and list entry number) that we want to write into. + * @param field The child field we are attempting to write into. + * @param allocator The BlockAllocator that can be used for allocating Arrow Buffers for fields which require conversion + * to Arrow Buff before being written. + * @param value The value to write. + * @note This method and its List complement violate the DRY mantra because ListWriter and StructWriter don't share + * a meaningful ancestor despite having identical methods. This requires us to either further wrap and abstract the writer + * or duplicate come code. In a future release we hope to have contributed a better option to Apache Arrow which allows + * us to simplify this method. + */ + @VisibleForTesting + protected static void writeStructValue(StructWriter writer, Field field, BufferAllocator allocator, Object value) + { + if (value == null) { + return; + } + + ArrowType type = field.getType(); + try { + switch (Types.getMinorTypeForArrowType(type)) { + case DATEMILLI: + if (value instanceof Date) { + writer.dateMilli(field.getName()).writeDateMilli(((Date) value).getTime()); + } + else { + writer.dateMilli(field.getName()).writeDateMilli((long) value); + } + break; + + case DATEDAY: + if (value instanceof Date) { + org.joda.time.Days days = org.joda.time.Days.daysBetween(EPOCH, + new org.joda.time.DateTime(((Date) value).getTime())); + writer.dateDay(field.getName()).writeDateDay(days.getDays()); + } + else if (value instanceof LocalDate) { + int days = (int) ((LocalDate) value).toEpochDay(); + writer.dateDay(field.getName()).writeDateDay(days); + } + else if (value instanceof Long) { + writer.dateDay(field.getName()).writeDateDay(((Long) value).intValue()); + } + else { + writer.dateDay(field.getName()).writeDateDay((int) value); + } + break; + case FLOAT8: + writer.float8(field.getName()).writeFloat8((double) value); + break; + case FLOAT4: + writer.float4(field.getName()).writeFloat4((float) value); + break; + case INT: + if (value != null && value instanceof Long) { + //This may seem odd at first but many frameworks (like Presto) use long as the preferred + //native java type for representing integers. We do this to keep type conversions simple. + writer.integer(field.getName()).writeInt(((Long) value).intValue()); + } + else { + writer.integer(field.getName()).writeInt((int) value); + } + break; + case TINYINT: + writer.tinyInt(field.getName()).writeTinyInt((byte) value); + break; + case SMALLINT: + writer.smallInt(field.getName()).writeSmallInt((short) value); + break; + case UINT1: + writer.uInt1(field.getName()).writeUInt1((byte) value); + break; + case UINT2: + writer.uInt2(field.getName()).writeUInt2((char) value); + break; + case UINT4: + writer.uInt4(field.getName()).writeUInt4((int) value); + break; + case UINT8: + writer.uInt8(field.getName()).writeUInt8((long) value); + break; + case BIGINT: + writer.bigInt(field.getName()).writeBigInt((long) value); + break; + case VARBINARY: + if (value instanceof ArrowBuf) { + ArrowBuf buf = (ArrowBuf) value; + writer.varBinary(field.getName()).writeVarBinary(0, buf.capacity(), buf); + } + else if (value instanceof byte[]) { + byte[] bytes = (byte[]) value; + try (ArrowBuf buf = allocator.buffer(bytes.length)) { + buf.writeBytes(bytes); + writer.varBinary(field.getName()).writeVarBinary(0, buf.readableBytes(), buf); + } + } + break; + case DECIMAL: + int scale = ((ArrowType.Decimal) type).getScale(); + if (value instanceof Double) { + int precision = ((ArrowType.Decimal) type).getPrecision(); + BigDecimal bdVal = new BigDecimal((double) value); + bdVal = bdVal.setScale(scale, RoundingMode.HALF_UP); + writer.decimal(field.getName(), scale, precision).writeDecimal(bdVal); + } + else { + BigDecimal scaledValue = ((BigDecimal) value).setScale(scale, RoundingMode.HALF_UP); + writer.decimal(field.getName()).writeDecimal(scaledValue); + } + break; + case VARCHAR: + if (value instanceof String) { + byte[] bytes = ((String) value).getBytes(Charsets.UTF_8); + try (ArrowBuf buf = allocator.buffer(bytes.length)) { + buf.writeBytes(bytes); + writer.varChar(field.getName()).writeVarChar(0, buf.readableBytes(), buf); + } + } + else if (value instanceof ArrowBuf) { + ArrowBuf buf = (ArrowBuf) value; + writer.varChar(field.getName()).writeVarChar(0, buf.readableBytes(), buf); + } + else if (value instanceof byte[]) { + byte[] bytes = (byte[]) value; + try (ArrowBuf buf = allocator.buffer(bytes.length)) { + buf.writeBytes(bytes); + writer.varChar(field.getName()).writeVarChar(0, buf.readableBytes(), buf); + } + } + break; + case BIT: + if (value instanceof Integer && (int) value > 0) { + writer.bit(field.getName()).writeBit(1); + } + else if (value instanceof Boolean && (boolean) value) { + writer.bit(field.getName()).writeBit(1); + } + else { + writer.bit(field.getName()).writeBit(0); + } + break; + default: + throw new IllegalArgumentException("Unknown type " + type); + } + } + catch (RuntimeException ex) { + throw new RuntimeException("Unable to write value for field " + field.getName() + " using value " + value, ex); + } + } + + /** + * Used to mark a particular cell as null. + * + * @param vector The FieldVector to write the null value to. + * @param pos The position (row) in the FieldVector to mark as null. + */ + private static void setNullValue(FieldVector vector, int pos) + { + switch (vector.getMinorType()) { + case DATEMILLI: + ((DateMilliVector) vector).setNull(pos); + break; + case DATEDAY: + ((DateDayVector) vector).setNull(pos); + break; + case FLOAT8: + ((Float8Vector) vector).setNull(pos); + break; + case FLOAT4: + ((Float4Vector) vector).setNull(pos); + break; + case INT: + ((IntVector) vector).setNull(pos); + break; + case TINYINT: + ((TinyIntVector) vector).setNull(pos); + break; + case SMALLINT: + ((SmallIntVector) vector).setNull(pos); + break; + case UINT1: + ((UInt1Vector) vector).setNull(pos); + break; + case UINT2: + ((UInt2Vector) vector).setNull(pos); + break; + case UINT4: + ((UInt4Vector) vector).setNull(pos); + break; + case UINT8: + ((UInt8Vector) vector).setNull(pos); + break; + case BIGINT: + ((BigIntVector) vector).setNull(pos); + break; + case VARBINARY: + ((VarBinaryVector) vector).setNull(pos); + break; + case DECIMAL: + ((DecimalVector) vector).setNull(pos); + break; + case VARCHAR: + ((VarCharVector) vector).setNull(pos); + break; + case BIT: + ((BitVector) vector).setNull(pos); + break; + default: + throw new IllegalArgumentException("Unknown type " + vector.getMinorType()); + } + } + + /** + * In some filtering situations it can be useful to 'unset' a row as an indication to a later processing stage + * that the row is irrelevant. The mechanism by which we 'unset' a row is actually field type specific and as such + * this method is not supported for all field types. + * + * @param row The row number to unset in the provided Block. + * @param block The Block where we'd like to unset the specified row. + */ + public static void unsetRow(int row, Block block) + { + for (FieldVector vector : block.getFieldVectors()) { + switch (vector.getMinorType()) { + case DATEDAY: + ((DateDayVector) vector).setNull(row); + break; + case DATEMILLI: + ((DateMilliVector) vector).setNull(row); + break; + case TINYINT: + ((TinyIntVector) vector).setNull(row); + break; + case UINT1: + ((UInt1Vector) vector).setNull(row); + break; + case SMALLINT: + ((SmallIntVector) vector).setNull(row); + break; + case UINT2: + ((UInt2Vector) vector).setNull(row); + break; + case UINT4: + ((UInt4Vector) vector).setNull(row); + break; + case INT: + ((IntVector) vector).setNull(row); + break; + case UINT8: + ((UInt8Vector) vector).setNull(row); + break; + case BIGINT: + ((BigIntVector) vector).setNull(row); + break; + case FLOAT4: + ((Float4Vector) vector).setNull(row); + break; + case FLOAT8: + ((Float8Vector) vector).setNull(row); + break; + case DECIMAL: + ((DecimalVector) vector).setNull(row); + break; + case VARBINARY: + ((VarBinaryVector) vector).setNull(row); + break; + case VARCHAR: + ((VarCharVector) vector).setNull(row); + break; + case BIT: + ((BitVector) vector).setNull(row); + break; + case STRUCT: + ((StructVector) vector).setNull(row); + break; + case LIST: + UnionListWriter writer = ((ListVector) vector).getWriter(); + writer.setPosition(row); + writer.startList(); + writer.endList(); + writer.setValueCount(0); + break; + default: + throw new IllegalArgumentException("Unknown type " + vector.getMinorType()); + } + } + } + + public static final org.joda.time.MutableDateTime EPOCH = new org.joda.time.MutableDateTime(); + + static { + EPOCH.setDate(0); + } + + private BlockUtils() {} + + private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); + + private static String bytesToHex(byte[] bytes) + { + char[] hexChars = new char[bytes.length * 2]; + for (int j = 0; j < bytes.length; j++) { + int v = bytes[j] & 0xFF; + hexChars[j * 2] = HEX_ARRAY[v >>> 4]; + hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F]; + } + return new String(hexChars); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/BlockWriter.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/BlockWriter.java new file mode 100644 index 0000000000..26423bd39a --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/BlockWriter.java @@ -0,0 +1,61 @@ +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connector.lambda.data; + +import com.amazonaws.athena.connector.lambda.domain.predicate.ConstraintEvaluator; + +/** + * Defines an abstraction that can be used to write to a Block without owning the lifecycle of the + * Block. + */ +public interface BlockWriter +{ + /** + * The interface you should implement for writing to a Block via + * the inverted ownership model offered by BlockWriter. + */ + interface RowWriter + { + /** + * Used to accumulate rows as part of a block. + * + * @param block The block you can add your row to. + * @param rowNum The row number in that block that the next row represents. + * @return The number of rows that were added + * @note We do not recommend writing more than 1 row per call. There are some use-cases which + * are made much simpler by being able to write a small number (<100) rows per call. These often + * relate to batched operators, scan side joins, or field expansions. Writing too many rows + * will result in errors related to Block size management and are implementation specific. + */ + int writeRows(Block block, int rowNum); + } + + /** + * Used to write rows via the BlockWriter. + * + * @param rowWriter The RowWriter that the BlockWriter should use to write rows into the Block(s) it is managing. + */ + void writeRows(RowWriter rowWriter); + + /** + * Provides access to the ConstraintEvaluator that will be applied to the generated Blocks. + */ + ConstraintEvaluator getConstraintEvaluator(); +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/FieldBuilder.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/FieldBuilder.java new file mode 100644 index 0000000000..99f7b8d10e --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/FieldBuilder.java @@ -0,0 +1,250 @@ +package com.amazonaws.athena.connector.lambda.data; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.arrow.vector.types.pojo.FieldType; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Convenience builder that can be used to create new Apache Arrow fields for common + * types more easily than alternative methods of construction, especially for complex types. + */ +public class FieldBuilder +{ + private final String name; + private final ArrowType type; + private final List children = new ArrayList<>(); + + /** + * Creates a FieldBuilder for a Field with the given name and type. + * + * @param name The name to use for the Field being built. + * @param type The type to use for the Field being built, most often one of STRUCT or LIST. + */ + private FieldBuilder(String name, ArrowType type) + { + this.name = name; + this.type = type; + } + + /** + * Creates a FieldBuilder for a Field with the given name and type. + * + * @param name The name to use for the Field being built. + * @param type The type to use for the Field being built, most often one of STRUCT or LIST. + * @return A new FieldBuilder for the specified name and type. + */ + public static FieldBuilder newBuilder(String name, ArrowType type) + { + return new FieldBuilder(name, type); + } + + /** + * Adds a new child field with the requested attributes. + * + * @param fieldName The name of the child field. + * @param type The type of the child field. + * @param children The children to add to the child field (empty list if no children desired). + * @return This FieldBuilder itself. + */ + public FieldBuilder addField(String fieldName, ArrowType type, List children) + { + this.children.add(new Field(fieldName, FieldType.nullable(type), children)); + return this; + } + + /** + * Adds the provided field as a child to the builder. + * + * @param child The child to add to the Field being built. + * @return This FieldBuilder itself. + */ + public FieldBuilder addField(Field child) + { + this.children.add(child); + return this; + } + + /** + * Adds a new VARCHAR child field with the given name to the builder. + * + * @param fieldName The name to use for the newly added child field. + * @return This FieldBuilder itself. + */ + public FieldBuilder addStringField(String fieldName) + { + this.children.add(new Field(fieldName, FieldType.nullable(Types.MinorType.VARCHAR.getType()), null)); + return this; + } + + /** + * Adds a new LIST child field with the given name to the builder. + * + * @param fieldName The name to use for the newly added child field. + * @param type The concrete type for values in the List + * @return This FieldBuilder itself. + */ + public FieldBuilder addListField(String fieldName, ArrowType type) + { + Field baseField = new Field("", FieldType.nullable(type), null); + Field field = new Field(fieldName, + FieldType.nullable(Types.MinorType.LIST.getType()), + Collections.singletonList(baseField)); + this.children.add(field); + return this; + } + + /** + * Adds a new INT child field with the given name to the builder. + * + * @param fieldName The name to use for the newly added child field. + * @return This FieldBuilder itself. + */ + public FieldBuilder addIntField(String fieldName) + { + this.children.add(new Field(fieldName, FieldType.nullable(Types.MinorType.INT.getType()), null)); + return this; + } + + /** + * Adds a new FLOAT8 child field with the given name to the builder. + * + * @param fieldName The name to use for the newly added child field. + * @return This FieldBuilder itself. + */ + public FieldBuilder addFloat8Field(String fieldName) + { + this.children.add(new Field(fieldName, FieldType.nullable(Types.MinorType.FLOAT8.getType()), null)); + return this; + } + + /** + * Adds a new BIGINT child field with the given name to the builder. + * + * @param fieldName The name to use for the newly added child field. + * @return This FieldBuilder itself. + */ + public FieldBuilder addBigIntField(String fieldName) + { + this.children.add(new Field(fieldName, FieldType.nullable(Types.MinorType.BIGINT.getType()), null)); + return this; + } + + /** + * Adds a new BIT child field with the given name to the builder. + * + * @param fieldName The name to use for the newly added child field. + * @return This FieldBuilder itself. + */ + public FieldBuilder addBitField(String fieldName) + { + this.children.add(new Field(fieldName, FieldType.nullable(Types.MinorType.BIT.getType()), null)); + return this; + } + + /** + * Adds a new TinyInt child field with the given name to the builder. + * + * @param fieldName The name to use for the newly added child field. + * @return This FieldBuilder itself. + */ + public FieldBuilder addTinyIntField(String fieldName) + { + this.children.add(new Field(fieldName, FieldType.nullable(Types.MinorType.TINYINT.getType()), null)); + return this; + } + + /** + * Adds a new SmallInt child field with the given name to the builder. + * + * @param fieldName The name to use for the newly added child field. + * @return This FieldBuilder itself. + */ + public FieldBuilder addSmallIntField(String fieldName) + { + this.children.add(new Field(fieldName, FieldType.nullable(Types.MinorType.SMALLINT.getType()), null)); + return this; + } + + /** + * Adds a new Float4 child field with the given name to the builder. + * + * @param fieldName The name to use for the newly added child field. + * @return This FieldBuilder itself. + */ + public FieldBuilder addFloat4Field(String fieldName) + { + this.children.add(new Field(fieldName, FieldType.nullable(Types.MinorType.FLOAT4.getType()), null)); + return this; + } + + /** + * Adds a new Decimal child field with the given name to the builder. + * + * @param fieldName The name to use for the newly added child field. + * @return This FieldBuilder itself. + */ + public FieldBuilder addDecimalField(String fieldName, int precision, int scale) + { + this.children.add(new Field(fieldName, FieldType.nullable(new ArrowType.Decimal(precision, scale)), null)); + return this; + } + + /** + * Adds a new DateDay child field with the given name to the builder. + * + * @param fieldName The name to use for the newly added child field. + * @return This FieldBuilder itself. + */ + public FieldBuilder addDateDayField(String fieldName) + { + this.children.add(new Field(fieldName, FieldType.nullable(Types.MinorType.DATEDAY.getType()), null)); + return this; + } + + /** + * Adds a new DateMilli child field with the given name to the builder. + * + * @param fieldName The name to use for the newly added child field. + * @return This FieldBuilder itself. + */ + public FieldBuilder addDateMilliField(String fieldName) + { + this.children.add(new Field(fieldName, FieldType.nullable(Types.MinorType.DATEMILLI.getType()), null)); + return this; + } + + /** + * Builds the fields. + * + * @return The newly constructed Field. + */ + public Field build() + { + return new Field(name, FieldType.nullable(type), children); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/FieldResolver.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/FieldResolver.java new file mode 100644 index 0000000000..8a6bb5d4b9 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/FieldResolver.java @@ -0,0 +1,69 @@ +package com.amazonaws.athena.connector.lambda.data; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Field; + +import java.util.List; +import java.util.Map; + +/** + * Assists in writing values for complex types like List and Struct by providing a way to extract child field + * values from the provided complex value. + */ +public interface FieldResolver +{ + /** + * Basic FieldResolver capable of resolving nested (or single level) Lists and Structs + * if the List values are iterable and the Structs values are represented + * as Map + * + * @note This This approach is relatively simple and convenient in terms of programming + * interface but sacrifices some performance due to Object overhead vs. using + * ApacheArrow directly. It is provided for basic usecases which don't have a high + * row count and also as a way to teach by example. For better performance, provide + * your own FieldResolver. And for even better performance, use ApacheArrow directly. + */ + FieldResolver DEFAULT = new FieldResolver() + { + public Object getFieldValue(Field field, Object value) + { + Types.MinorType minorType = Types.getMinorTypeForArrowType(field.getType()); + if (minorType == Types.MinorType.LIST) { + return ((List) value).iterator(); + } + else if (value instanceof Map) { + return ((Map) value).get(field.getName()); + } + throw new RuntimeException("Expected LIST type but found " + minorType); + } + }; + + /** + * Used to extract a value for the given Field from the provided value. + * + * @param field The field that we would like to extract from the provided value. + * @param value The complex value we'd like to extract the provided field from. + * @return The value to use for the given field. + */ + Object getFieldValue(Field field, Object value); +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/RecordBatchSerDe.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/RecordBatchSerDe.java new file mode 100644 index 0000000000..7982161ed6 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/RecordBatchSerDe.java @@ -0,0 +1,90 @@ +package com.amazonaws.athena.connector.lambda.data; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import org.apache.arrow.memory.BufferAllocator; +import org.apache.arrow.vector.ipc.ReadChannel; +import org.apache.arrow.vector.ipc.WriteChannel; +import org.apache.arrow.vector.ipc.message.ArrowRecordBatch; +import org.apache.arrow.vector.ipc.message.MessageSerializer; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.channels.Channels; + +/** + * used to serialize and deserialize ArrowRecordBatch. + */ +public class RecordBatchSerDe +{ + private final BlockAllocator allocator; + + public RecordBatchSerDe(BlockAllocator allocator) + { + this.allocator = allocator; + } + + /** + * Serialized the provided ArrowRecordBatch to the provided OutputStream and closes the batch once + * it is fully written to the OutputStream. + * + * @param batch The ArrowRecordBatch to serialize. + * @param out The OutputStream to write to. + * @throws IOException + */ + public void serialize(ArrowRecordBatch batch, OutputStream out) + throws IOException + { + try { + MessageSerializer.serialize(new WriteChannel(Channels.newChannel(out)), batch); + } + finally { + batch.close(); + } + } + + /** + * Attempts to deserialize the provided byte[] into an ArrowRecordBatch. + * + * @param in The byte[] that is expected to contain a serialized ArrowRecordBatch. + * @return The resulting ArrowRecordBatch if the byte[] contains a valid ArrowRecordBatch. + * @throws IOException + */ + public ArrowRecordBatch deserialize(byte[] in) + throws IOException + { + ArrowRecordBatch batch = null; + try { + return allocator.registerBatch((BufferAllocator root) -> + (ArrowRecordBatch) MessageSerializer.deserializeMessageBatch( + new ReadChannel(Channels.newChannel(new ByteArrayInputStream(in))), + root) + ); + } + catch (Exception ex) { + if (batch != null) { + batch.close(); + } + throw ex; + } + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/S3BlockSpillReader.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/S3BlockSpillReader.java new file mode 100644 index 0000000000..48806b99dc --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/S3BlockSpillReader.java @@ -0,0 +1,118 @@ +package com.amazonaws.athena.connector.lambda.data; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.domain.spill.S3SpillLocation; +import com.amazonaws.athena.connector.lambda.security.AesGcmBlockCrypto; +import com.amazonaws.athena.connector.lambda.security.BlockCrypto; +import com.amazonaws.athena.connector.lambda.security.EncryptionKey; +import com.amazonaws.athena.connector.lambda.security.NoOpBlockCrypto; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.S3Object; +import com.google.common.io.ByteStreams; +import org.apache.arrow.vector.types.pojo.Schema; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; + +import static java.util.Objects.requireNonNull; + +public class S3BlockSpillReader +{ + private static final Logger logger = LoggerFactory.getLogger(S3BlockSpillReader.class); + + private final AmazonS3 amazonS3; + private final BlockAllocator allocator; + + public S3BlockSpillReader(AmazonS3 amazonS3, BlockAllocator allocator) + { + this.amazonS3 = requireNonNull(amazonS3, "amazonS3 was null"); + this.allocator = requireNonNull(allocator, "allocator was null"); + } + + /** + * Reads a spilled block. + * + * @param spillLocation The location to read the spilled Block from. + * @param key The encryption key to use when reading the spilled Block. + * @param schema The Schema to use when deserializing the spilled Block. + * @return The Block stored at the spill location. + */ + public Block read(S3SpillLocation spillLocation, EncryptionKey key, Schema schema) + { + S3Object fullObject = null; + try { + logger.debug("read: Started reading block from S3"); + fullObject = amazonS3.getObject(spillLocation.getBucket(), spillLocation.getKey()); + logger.debug("read: Completed reading block from S3"); + BlockCrypto blockCrypto = (key != null) ? new AesGcmBlockCrypto(allocator) : new NoOpBlockCrypto(allocator); + Block block = blockCrypto.decrypt(key, ByteStreams.toByteArray(fullObject.getObjectContent()), schema); + logger.debug("read: Completed decrypting block of size."); + return block; + } + catch (IOException ex) { + throw new RuntimeException(ex); + } + finally { + if (fullObject != null) { + try { + fullObject.close(); + } + catch (IOException ex) { + logger.warn("read: Exception while closing S3 object", ex); + } + } + } + } + + /** + * Reads spilled data as a byte[]. + * + * @param spillLocation The location to read the spilled Block from. + * @param key The encryption key to use when reading the spilled Block. + * @return The Block stored at the spill location. + */ + public byte[] read(S3SpillLocation spillLocation, EncryptionKey key) + { + S3Object fullObject = null; + try { + logger.debug("read: Started reading block from S3"); + fullObject = amazonS3.getObject(spillLocation.getBucket(), spillLocation.getKey()); + logger.debug("read: Completed reading block from S3"); + BlockCrypto blockCrypto = (key != null) ? new AesGcmBlockCrypto(allocator) : new NoOpBlockCrypto(allocator); + return blockCrypto.decrypt(key, ByteStreams.toByteArray(fullObject.getObjectContent())); + } + catch (IOException ex) { + throw new RuntimeException(ex); + } + finally { + if (fullObject != null) { + try { + fullObject.close(); + } + catch (IOException ex) { + logger.warn("read: Exception while closing S3 object", ex); + } + } + } + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/S3BlockSpiller.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/S3BlockSpiller.java new file mode 100644 index 0000000000..85d44117de --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/S3BlockSpiller.java @@ -0,0 +1,422 @@ +package com.amazonaws.athena.connector.lambda.data; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.domain.predicate.ConstraintEvaluator; +import com.amazonaws.athena.connector.lambda.domain.spill.S3SpillLocation; +import com.amazonaws.athena.connector.lambda.domain.spill.SpillLocation; +import com.amazonaws.athena.connector.lambda.security.AesGcmBlockCrypto; +import com.amazonaws.athena.connector.lambda.security.BlockCrypto; +import com.amazonaws.athena.connector.lambda.security.EncryptionKey; +import com.amazonaws.athena.connector.lambda.security.NoOpBlockCrypto; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.ObjectMetadata; +import com.amazonaws.services.s3.model.S3Object; +import com.google.common.io.ByteStreams; +import org.apache.arrow.vector.types.pojo.Schema; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.StampedLock; + +import static java.util.Objects.requireNonNull; + +/** + * Implementation of BlockSpiller which spills Blocks from large results to S3 with optional AES-GCM encryption. + * + * @note The size at which this implementation will spill to S3 are configured using SpillConfig. + */ +public class S3BlockSpiller + implements AutoCloseable, BlockSpiller +{ + private static final Logger logger = LoggerFactory.getLogger(S3BlockSpiller.class); + //Used to control how long we will wait for background spill threads to exit. + private static final long ASYNC_SHUTDOWN_MILLIS = 10_000; + //The default max number of rows that are allowed to be written per call to writeRows(...) + private static final int MAX_ROWS_PER_CALL = 100; + + //Used to write to S3 + private final AmazonS3 amazonS3; + //Used to optionally encrypt Blocks. + private final BlockCrypto blockCrypto; + //Used to create new blocks. + private final BlockAllocator allocator; + //Controls how/when/where/if this implementation will spill to S3. + private final SpillConfig spillConfig; + //The schema to use for Blocks. + private final Schema schema; + //The max number of rows that are allowed to be written per call to writeRows(...) + private final long maxRowsPerCall; + //If we spilled, the spill locations are kept here. + private final List spillLocations = new ArrayList<>(); + //Reference to the in progress Block. + private final AtomicReference inProgressBlock = new AtomicReference<>(); + //Allows a degree of pipelining to take place so we don't block reading from the source + //while we are spilling. + private final ExecutorService asyncSpillPool; + //Allows us to provide thread safety between async spill completion and calls to getSpill status + private final ReadWriteLock spillLock = new StampedLock().asReadWriteLock(); + //Used to create monotonically increasing spill locations, if the locations are not + //monotonically increasing then read performance may suffer as the engine's ability to + //pre-fetch/pipeline reads before write are completed may use this characteristic of the writes + //to ensure consistency + private final AtomicLong spillNumber = new AtomicLong(0); + //Holder that is used to surface any exceptions encountered in our background spill threads. + private final AtomicReference asyncException = new AtomicReference<>(null); + // + private final ConstraintEvaluator constraintEvaluator; + + /** + * Constructor which uses the default maxRowsPerCall. + * + * @param amazonS3 AmazonS3 client to use for writing to S3. + * @param spillConfig The spill config for this instance. Includes things like encryption key, s3 path, etc... + * @param allocator The BlockAllocator to use when creating blocks. + * @param schema The schema for blocks that should be written. + * @param constraintEvaluator The ConstraintEvaluator that should be used to constrain writes. + */ + public S3BlockSpiller(AmazonS3 amazonS3, + SpillConfig spillConfig, + BlockAllocator allocator, + Schema schema, + ConstraintEvaluator constraintEvaluator) + { + this(amazonS3, spillConfig, allocator, schema, constraintEvaluator, MAX_ROWS_PER_CALL); + } + + /** + * Constructs a new S3BlockSpiller. + * + * @param amazonS3 AmazonS3 client to use for writing to S3. + * @param spillConfig The spill config for this instance. Includes things like encryption key, s3 path, etc... + * @param allocator The BlockAllocator to use when creating blocks. + * @param schema The schema for blocks that should be written. + * @param constraintEvaluator The ConstraintEvaluator that should be used to constrain writes. + * @param maxRowsPerCall The max number of rows to allow callers to write in one call. + */ + public S3BlockSpiller(AmazonS3 amazonS3, + SpillConfig spillConfig, + BlockAllocator allocator, + Schema schema, + ConstraintEvaluator constraintEvaluator, + int maxRowsPerCall) + { + this.amazonS3 = requireNonNull(amazonS3, "amazonS3 was null"); + this.spillConfig = requireNonNull(spillConfig, "spillConfig was null"); + this.allocator = requireNonNull(allocator, "allocator was null"); + this.schema = requireNonNull(schema, "schema was null"); + this.blockCrypto = (spillConfig.getEncryptionKey() != null) ? new AesGcmBlockCrypto(allocator) : new NoOpBlockCrypto(allocator); + asyncSpillPool = (spillConfig.getNumSpillThreads() <= 0) ? null : + Executors.newFixedThreadPool(spillConfig.getNumSpillThreads()); + this.maxRowsPerCall = maxRowsPerCall; + this.constraintEvaluator = constraintEvaluator; + } + + /** + * Provides access to the constraint evaluator used to constrain blocks written via this BlockSpiller. + * + * @return + */ + @Override + public ConstraintEvaluator getConstraintEvaluator() + { + return constraintEvaluator; + } + + /** + * Used to write rows via the BlockWriter. + * + * @param rowWriter The RowWriter that the BlockWriter should use to write rows into the Block(s) it is managing. + * @see BlockSpiller + */ + public void writeRows(RowWriter rowWriter) + { + ensureInit(); + + Block block = inProgressBlock.get(); + int rowCount = block.getRowCount(); + + int rows = rowWriter.writeRows(block, rowCount); + + if (rows > maxRowsPerCall) { + throw new RuntimeException("Call generated more than " + maxRowsPerCall + "rows. Generating " + + "too many rows per call to writeRows(...) can result in blocks that exceed the max size."); + } + if (rows > 0) { + block.setRowCount(rowCount + rows); + } + + if (block.getSize() > spillConfig.getMaxBlockBytes()) { + logger.info("writeRow: Spilling block with {} rows and {} bytes and config {} bytes", + new Object[] {block.getRowCount(), block.getSize(), spillConfig.getMaxBlockBytes()}); + spillBlock(block); + inProgressBlock.set(this.allocator.createBlock(this.schema)); + inProgressBlock.get().constrain(constraintEvaluator); + } + } + + /** + * Used to tell if any blocks were spilled or if the response can be inline. + * + * @return True is spill occurred, false otherwise. + */ + public boolean spilled() + { + if (asyncException.get() != null) { + throw asyncException.get(); + } + + //We use the write lock because we want this to have exclusive access to the state + Lock lock = spillLock.writeLock(); + try { + lock.lock(); + ensureInit(); + Block block = inProgressBlock.get(); + return !spillLocations.isEmpty() || block.getSize() >= spillConfig.getMaxInlineBlockSize(); + } + finally { + lock.unlock(); + } + } + + /** + * If spilled() returns false this can be used to access the block. + * + * @return Block to be inlined in the response. + * @Throws RuntimeException if blocks were spilled and this method is called. + */ + public Block getBlock() + { + if (spilled()) { + throw new RuntimeException("Blocks have spilled, calls to getBlock not permitted. use getSpillLocations instead."); + } + + logger.info("getBlock: Inline Block size[{}] bytes vs {}", inProgressBlock.get().getSize(), spillConfig.getMaxInlineBlockSize()); + return inProgressBlock.get(); + } + + /** + * If spilled() returns true this can be used to access the spill locations of all blocks. + * + * @return List of spill locations. + * @Throws RuntimeException if blocks were not spilled and this method is called. + */ + public List getSpillLocations() + { + if (!spilled()) { + throw new RuntimeException("Blocks have not spilled, calls to getSpillLocations not permitted. use getBlock instead."); + } + + Lock lock = spillLock.writeLock(); + try { + /** + * Flush the in-progress block in nessesary. + */ + Block block = inProgressBlock.get(); + if (block.getRowCount() > 0) { + logger.info("getSpillLocations: Spilling final block with {} rows and {} bytes and config {} bytes", + new Object[] {block.getRowCount(), block.getSize(), spillConfig.getMaxBlockBytes()}); + + spillBlock(block); + + inProgressBlock.set(this.allocator.createBlock(this.schema)); + inProgressBlock.get().constrain(constraintEvaluator); + } + + lock.lock(); + return spillLocations; + } + finally { + lock.unlock(); + } + } + + /** + * Frees any resources held by this BlockSpiller. + * + * @see BlockSpiller + */ + public void close() + { + if (asyncSpillPool == null) { + return; + } + + asyncSpillPool.shutdown(); + try { + if (!asyncSpillPool.awaitTermination(ASYNC_SHUTDOWN_MILLIS, TimeUnit.MILLISECONDS)) { + asyncSpillPool.shutdownNow(); + } + } + catch (InterruptedException e) { + Thread.currentThread().interrupt(); + asyncSpillPool.shutdownNow(); + } + } + + /** + * Writes (aka spills) a Block. + */ + protected SpillLocation write(Block block) + { + try { + S3SpillLocation spillLocation = makeSpillLocation(); + EncryptionKey encryptionKey = spillConfig.getEncryptionKey(); + + logger.info("write: Started encrypting block for write to {}", spillLocation); + byte[] bytes = blockCrypto.encrypt(encryptionKey, block); + + logger.info("write: Started spilling block of size {} bytes", bytes.length); + amazonS3.putObject(spillLocation.getBucket(), + spillLocation.getKey(), + new ByteArrayInputStream(bytes), + new ObjectMetadata()); + logger.info("write: Completed spilling block of size {} bytes", bytes.length); + + return spillLocation; + } + catch (RuntimeException ex) { + asyncException.compareAndSet(null, ex); + logger.warn("write: Encountered error while writing block.", ex); + throw ex; + } + } + + /** + * Reads a spilled block. + * + * @param spillLocation The location to read the spilled Block from. + * @param key The encryption key to use when reading the spilled Block. + * @param schema The Schema to use when deserializing the spilled Block. + * @return The Block stored at the spill location. + */ + protected Block read(S3SpillLocation spillLocation, EncryptionKey key, Schema schema) + { + try { + logger.debug("write: Started reading block from S3"); + S3Object fullObject = amazonS3.getObject(spillLocation.getBucket(), spillLocation.getKey()); + logger.debug("write: Completed reading block from S3"); + Block block = blockCrypto.decrypt(key, ByteStreams.toByteArray(fullObject.getObjectContent()), schema); + logger.debug("write: Completed decrypting block of size."); + return block; + } + catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + /** + * Spills a block, potentially asynchronously depending on the settings. + * + * @param block The Block to spill. + */ + private void spillBlock(Block block) + { + if (asyncSpillPool != null) { + //We use the read lock here because we want to allow these in parallel, its a bit counter intuitive + Lock lock = spillLock.readLock(); + try { + //We lock before going async but unlock after spilling in the async thread, this makes it easy to use + //the ReadWrite lock to tell if all spills are completed without killing the thread pool. + lock.lock(); + asyncSpillPool.submit(() -> { + try { + SpillLocation spillLocation = write(block); + spillLocations.add(spillLocation); + //Free the memory from the previous block since it has been spilled + safeClose(block); + } + finally { + lock.unlock(); + } + }); + } + catch (Exception ex) { + //If we hit an exception, make sure we unlock to avoid a deadlock before throwing. + lock.unlock(); + throw ex; + } + } + else { + SpillLocation spillLocation = write(block); + spillLocations.add(spillLocation); + safeClose(block); + } + } + + /** + * Ensures that the initial Block is initialized. + */ + private void ensureInit() + { + if (inProgressBlock.get() == null) { + //Create the initial block + inProgressBlock.set(this.allocator.createBlock(this.schema)); + inProgressBlock.get().constrain(constraintEvaluator); + } + } + + /** + * This needs to be thread safe and generate locations in a format of: + * location.0 + * location.1 + * location.2 + *

+ * The read engine may elect to exploit this naming convention to speed up the pipelining of + * reads while the spiller is still writing. Violating this convention may reduce performance + * or increase calls to S3. + */ + private S3SpillLocation makeSpillLocation() + { + S3SpillLocation splitSpillLocation = (S3SpillLocation) spillConfig.getSpillLocation(); + if (!splitSpillLocation.isDirectory()) { + throw new RuntimeException("Split's SpillLocation must be a directory because multiple blocks may be spilled."); + } + String blockKey = splitSpillLocation.getKey() + "." + spillNumber.getAndIncrement(); + return new S3SpillLocation(splitSpillLocation.getBucket(), blockKey, false); + } + + /** + * Closes the supplied AutoCloseable and remaps any actions to Runtime. + * + * @param block The Block to close. + */ + private void safeClose(AutoCloseable block) + { + try { + block.close(); + } + catch (Exception ex) { + throw new RuntimeException(ex); + } + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/SchemaAware.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/SchemaAware.java new file mode 100644 index 0000000000..d688d1733a --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/SchemaAware.java @@ -0,0 +1,71 @@ +package com.amazonaws.athena.connector.lambda.data; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.arrow.vector.types.pojo.Schema; + +import java.util.List; +import java.util.Map; + +/** + * Defines a component that is aware of Apache Arrow Schema. + */ +public abstract class SchemaAware +{ + /** + * Provides access to the Schema object. + * + * @return The Schema currently being used by this object. + */ + protected abstract Schema internalGetSchema(); + + /** + * Provides access to the Fields on the Schema currently being used by this Object. + * + * @return The list of fields. + */ + public List getFields() + { + return internalGetSchema().getFields(); + } + + /** + * Provides access to metadata stored on the Schema currently being used by this Object. + * + * @param key The metadata key to lookup. + * @return The value associated with that key in the Schema's metadata, null if no such key exists. + */ + public String getMetaData(String key) + { + return internalGetSchema().getCustomMetadata().get(key); + } + + /** + * Provides access to all avaialable metadata on the Schema. + * + * @return All metadata key-value pairs as a map. + */ + public Map getMetaData() + { + return internalGetSchema().getCustomMetadata(); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/SchemaBuilder.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/SchemaBuilder.java new file mode 100644 index 0000000000..f1892ebccd --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/SchemaBuilder.java @@ -0,0 +1,304 @@ +package com.amazonaws.athena.connector.lambda.data; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.arrow.vector.types.pojo.FieldType; +import org.apache.arrow.vector.types.pojo.Schema; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Convenience builder that can be used to create new Apache Arrow Schema for common + * types more easily than alternative methods of construction, especially for complex types. + */ +public class SchemaBuilder +{ + private final ImmutableList.Builder fields = ImmutableList.builder(); + private final ImmutableMap.Builder metadata = ImmutableMap.builder(); + private final Map nestedFieldBuilderMap = new HashMap<>(); + + public SchemaBuilder addField(Field field) + { + fields.add(field); + return this; + } + + /** + * Adds a new Field with the provided details to the Schema as a top-level Field with no children. + * + * @param fieldName The name of the field to add. + * @param type The type of the field to add. + * @return This SchemaBuilder itself. + */ + public SchemaBuilder addField(String fieldName, ArrowType type) + { + fields.add(new Field(fieldName, FieldType.nullable(type), null)); + return this; + } + + /** + * Adds a new Field with the provided details to the Schema as a top-level Field. + * + * @param fieldName The name of the field to add. + * @param type The type of the field to add. + * @param children The list of child fields to add to the new Field. + * @return This SchemaBuilder itself. + */ + public SchemaBuilder addField(String fieldName, ArrowType type, List children) + { + fields.add(new Field(fieldName, FieldType.nullable(type), children)); + return this; + } + + /** + * Adds a new STRUCT Field to the Schema as a top-level Field. + * + * @param fieldName The name of the field to add. + * @return This SchemaBuilder itself. + */ + public SchemaBuilder addStructField(String fieldName) + { + nestedFieldBuilderMap.put(fieldName, FieldBuilder.newBuilder(fieldName, Types.MinorType.STRUCT.getType())); + return this; + } + + /** + * Adds a new LIST Field to the Schema as a top-level Field. + * + * @param fieldName The name of the field to add. + * @param type The concrete type of the values that are held within the list. + * @return This SchemaBuilder itself. + */ + public SchemaBuilder addListField(String fieldName, ArrowType type) + { + fields.add(new Field(fieldName, FieldType.nullable(Types.MinorType.LIST.getType()), + Collections.singletonList(new Field("", FieldType.nullable(type), null)))); + return this; + } + + /** + * Adds a new Field as a child of the requested top-level parent field. + * + * @param parent The name of the pre-existing top-level parent field to add a child field to. + * @param child The name of the new child field. + * @param type The type of the new child field to add. + * @return This SchemaBuilder itself. + * @note For more complex nesting, please use FieldBuilder. + */ + public SchemaBuilder addChildField(String parent, String child, ArrowType type) + { + nestedFieldBuilderMap.get(parent).addField(child, type, null); + return this; + } + + /** + * Adds a new Field as a child of the requested top-level parent field. + * + * @param parent The name of the pre-existing top-level parent field to add a child field to. + * @param child The child field to add to the parent. + * @return This SchemaBuilder itself. + * @note For more complex nesting, please use FieldBuilder. + */ + public SchemaBuilder addChildField(String parent, Field child) + { + nestedFieldBuilderMap.get(parent).addField(child); + return this; + } + + /** + * Adds a new VARCHAR Field to the Schema as a top-level Field with no children. + * + * @param fieldName The name of the field to add. + * @return This SchemaBuilder itself. + */ + public SchemaBuilder addStringField(String fieldName) + { + fields.add(new Field(fieldName, FieldType.nullable(Types.MinorType.VARCHAR.getType()), null)); + return this; + } + + /** + * Adds a new INT Field to the Schema as a top-level Field with no children. + * + * @param fieldName The name of the field to add. + * @return This SchemaBuilder itself. + */ + public SchemaBuilder addIntField(String fieldName) + { + fields.add(new Field(fieldName, FieldType.nullable(Types.MinorType.INT.getType()), null)); + return this; + } + + /** + * Adds a new TINYINT Field to the Schema as a top-level Field with no children. + * + * @param fieldName The name of the field to add. + * @return This SchemaBuilder itself. + */ + public SchemaBuilder addTinyIntField(String fieldName) + { + fields.add(new Field(fieldName, FieldType.nullable(Types.MinorType.TINYINT.getType()), null)); + return this; + } + + /** + * Adds a new SMALLINT Field to the Schema as a top-level Field with no children. + * + * @param fieldName The name of the field to add. + * @return This SchemaBuilder itself. + */ + public SchemaBuilder addSmallIntField(String fieldName) + { + fields.add(new Field(fieldName, FieldType.nullable(Types.MinorType.SMALLINT.getType()), null)); + return this; + } + + /** + * Adds a new FLOAT8 Field to the Schema as a top-level Field with no children. + * + * @param fieldName The name of the field to add. + * @return This SchemaBuilder itself. + */ + public SchemaBuilder addFloat8Field(String fieldName) + { + fields.add(new Field(fieldName, FieldType.nullable(Types.MinorType.FLOAT8.getType()), null)); + return this; + } + + /** + * Adds a new FLOAT4 Field to the Schema as a top-level Field with no children. + * + * @param fieldName The name of the field to add. + * @return This SchemaBuilder itself. + */ + public SchemaBuilder addFloat4Field(String fieldName) + { + fields.add(new Field(fieldName, FieldType.nullable(Types.MinorType.FLOAT4.getType()), null)); + return this; + } + + /** + * Adds a new BIGINT Field to the Schema as a top-level Field with no children. + * + * @param fieldName The name of the field to add. + * @return This SchemaBuilder itself. + */ + public SchemaBuilder addBigIntField(String fieldName) + { + fields.add(new Field(fieldName, FieldType.nullable(Types.MinorType.BIGINT.getType()), null)); + return this; + } + + /** + * Adds a new BIT Field to the Schema as a top-level Field with no children. + * + * @param fieldName The name of the field to add. + * @return This SchemaBuilder itself. + */ + public SchemaBuilder addBitField(String fieldName) + { + fields.add(new Field(fieldName, FieldType.nullable(Types.MinorType.BIT.getType()), null)); + return this; + } + + /** + * Adds a new DECIMAL Field to the Schema as a top-level Field with no children. + * + * @param fieldName The name of the field to add. + * @param precision The precision to use for the new decimal field. + * @param scale The scale to use for the new decimal field. + * @return This SchemaBuilder itself. + */ + public SchemaBuilder addDecimalField(String fieldName, int precision, int scale) + { + fields.add(new Field(fieldName, FieldType.nullable(new ArrowType.Decimal(precision, scale)), null)); + return this; + } + + /** + * Adds a new DateDay Field to the Schema as a top-level Field with no children. + * + * @param fieldName The name of the field to add. + * @return This SchemaBuilder itself. + */ + public SchemaBuilder addDateDayField(String fieldName) + { + fields.add(new Field(fieldName, FieldType.nullable(Types.MinorType.DATEDAY.getType()), null)); + return this; + } + + /** + * Adds a new DateMilli Field to the Schema as a top-level Field with no children. + * + * @param fieldName The name of the field to add. + * @return This SchemaBuilder itself. + */ + public SchemaBuilder addDateMilliField(String fieldName) + { + fields.add(new Field(fieldName, FieldType.nullable(Types.MinorType.DATEMILLI.getType()), null)); + return this; + } + + /** + * Adds the provided metadata to the Schema. + * + * @param key The key of the metadata to add. + * @param value The value of the metadata to add. + * @return This SchemaBuilder itself. + */ + public SchemaBuilder addMetadata(String key, String value) + { + metadata.put(key, value); + return this; + } + + /** + * Creates a new SchemaBuilder. + * + * @return A new SchemaBuilder. + */ + public static SchemaBuilder newBuilder() + { + return new SchemaBuilder(); + } + + /** + * Builds an Apache Arrow Schema from the collected metadata and fields. + * + * @return A new Apache Arrow Schema. + * @note Attempting to reuse this builder will have unexpected side affects. + */ + public Schema build() + { + for (FieldBuilder next : nestedFieldBuilderMap.values()) { + fields.add(next.build()); + } + return new Schema(fields.build(), metadata.build()); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/SchemaSerDe.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/SchemaSerDe.java new file mode 100644 index 0000000000..0b81a61157 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/SchemaSerDe.java @@ -0,0 +1,64 @@ +package com.amazonaws.athena.connector.lambda.data; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import org.apache.arrow.vector.ipc.ReadChannel; +import org.apache.arrow.vector.ipc.WriteChannel; +import org.apache.arrow.vector.ipc.message.MessageSerializer; +import org.apache.arrow.vector.types.pojo.Schema; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.channels.Channels; + +/** + * Used to serialize and deserialize Apache Arrow Schema objects. + */ +public class SchemaSerDe +{ + /** + * Serialized the provided Schema to the provided OutputStream. + * + * @param schema The Schema to serialize. + * @param out The OutputStream to write to. + * @throws IOException + */ + public void serialize(Schema schema, OutputStream out) + throws IOException + { + MessageSerializer.serialize(new WriteChannel(Channels.newChannel(out)), schema); + } + + /** + * Attempts to deserialize a Schema from the provided InputStream. + * + * @param in The InputStream that is expected to contain a serialized Schema. + * @return The resulting Schema if the InputStream contains a valid Schema. + * @throws IOException + * @note This method does _not_ close the input stream and also reads the InputStream to the end. + */ + public Schema deserialize(InputStream in) + throws IOException + { + return MessageSerializer.deserializeSchema(new ReadChannel(Channels.newChannel(in))); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/SimpleBlockWriter.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/SimpleBlockWriter.java new file mode 100644 index 0000000000..e8d38128d4 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/SimpleBlockWriter.java @@ -0,0 +1,71 @@ +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connector.lambda.data; + +import com.amazonaws.athena.connector.lambda.domain.predicate.ConstraintEvaluator; + +/** + * Used to write a single Block using the BlockWriter programming model. + * + * @see BlockWriter + */ +public class SimpleBlockWriter + implements BlockWriter +{ + private final Block block; + + /** + * Basic constructor using a pre-allocated Block. + * + * @param block The Block to write into. + */ + public SimpleBlockWriter(Block block) + { + this.block = block; + } + + /** + * Used to write rows into the Block that is managed by this BlockWriter. + * + * @param rowWriter The RowWriter that the BlockWriter should use to write rows into the Block(s) it is managing. + * @See BlockWriter + */ + public void writeRows(BlockWriter.RowWriter rowWriter) + { + int rowCount = block.getRowCount(); + + int rows = rowWriter.writeRows(block, rowCount); + + if (rows > 0) { + block.setRowCount(rowCount + rows); + } + } + + /** + * Provides access to the ConstraintEvaluator that will be applied to the generated Blocks. + * + * @See BlockWriter + */ + @Override + public ConstraintEvaluator getConstraintEvaluator() + { + return block.getConstraintEvaluator(); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/SpillConfig.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/SpillConfig.java new file mode 100644 index 0000000000..21ad0f7d5e --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/SpillConfig.java @@ -0,0 +1,180 @@ +package com.amazonaws.athena.connector.lambda.data; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.domain.spill.SpillLocation; +import com.amazonaws.athena.connector.lambda.security.EncryptionKey; + +import static java.util.Objects.requireNonNull; + +/** + * Used to configure Spill functionality. + */ +public class SpillConfig +{ + //The default number of threads to use for async spill operations. 0 indicates that the calling thread should be used. + private static final int DEFAULT_SPILL_THREADS = 1; + //The encryption key that should be used to read/write spilled data. If null, encryption is disabled. + private final EncryptionKey encryptionKey; + //The location where the data is spilled. + private final SpillLocation spillLocation; + //The id of the request. + private final String requestId; + //The max bytes that can be in a single Block. + private final long maxBlockBytes; + //The max bytes that can be in an inline (non-spilled) Block. + private final long maxInlineBlockSize; + //The default number of threads to use for async spill operations. 0 indicates that the calling thread should be used. + private final int numSpillThreads; + + private SpillConfig(Builder builder) + { + encryptionKey = builder.encryptionKey; + spillLocation = requireNonNull(builder.spillLocation, "spillLocation was null"); + requestId = requireNonNull(builder.requestId, "requestId was null"); + maxBlockBytes = builder.maxBlockBytes; + maxInlineBlockSize = builder.maxInlineBlockSize; + numSpillThreads = builder.numSpillThreads; + } + + /** + * Gets the Encryption key to use when reading/writing data to the spill location. + * + * @return The EncryptionKey. + * @note If null, spill encryption is disabled. + */ + public EncryptionKey getEncryptionKey() + { + return encryptionKey; + } + + /** + * Gets the SpillLocation, if spill is enabled. + * + * @return The SpillLocation + */ + public SpillLocation getSpillLocation() + { + return spillLocation; + } + + /** + * Gets the request Id, typically the Athena query ID. + * @return The request id. + */ + public String getRequestId() + { + return requestId; + } + + /** + * Gets max number of bytes a spilled Block can contain. + * @return The number of bytes. + */ + public long getMaxBlockBytes() + { + return maxBlockBytes; + } + + /** + * Gets max number of bytes an inline Block can contain. + * @return The number of bytes. + */ + public long getMaxInlineBlockSize() + { + return maxInlineBlockSize; + } + + /** + * Gets the number of threads the BlockSpiller can use. + * @return The number of threads. + */ + public int getNumSpillThreads() + { + return numSpillThreads; + } + + public static Builder newBuilder() + { + return new Builder(); + } + + public static Builder newBuilder(SpillConfig copy) + { + Builder builder = new Builder(); + builder.encryptionKey = copy.getEncryptionKey(); + builder.maxBlockBytes = copy.getMaxBlockBytes(); + return builder; + } + + public static final class Builder + { + private EncryptionKey encryptionKey; + private String requestId; + private SpillLocation spillLocation; + private long maxBlockBytes; + private long maxInlineBlockSize; + private int numSpillThreads = DEFAULT_SPILL_THREADS; + + private Builder() {} + + public Builder withEncryptionKey(EncryptionKey val) + { + encryptionKey = val; + return this; + } + + public Builder withRequestId(String val) + { + requestId = val; + return this; + } + + public Builder withSpillLocation(SpillLocation val) + { + spillLocation = val; + return this; + } + + public Builder withNumSpillThreads(int val) + { + numSpillThreads = val; + return this; + } + + public Builder withMaxBlockBytes(long val) + { + maxBlockBytes = val; + return this; + } + + public Builder withMaxInlineBlockBytes(long val) + { + maxInlineBlockSize = val; + return this; + } + + public SpillConfig build() + { + return new SpillConfig(this); + } + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/projectors/ArrowValueProjector.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/projectors/ArrowValueProjector.java new file mode 100644 index 0000000000..61d5185480 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/projectors/ArrowValueProjector.java @@ -0,0 +1,36 @@ +package com.amazonaws.athena.connector.lambda.data.projectors; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +/** + * Implementation of this interface is expected to project Arrow data into Java objects. The implementation is + * expected to take into a fieldReader during initialization. Each call of {@link #project(int) project} would + * project one Arrow datum to a Java Object the object. + */ +public interface ArrowValueProjector +{ + /** + * Projects Arrow datum into a matching Java object + * @param pos the position/row to project to + * @return The corresponding Java object matching the Arrow datum. + */ + Object project(int pos); +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/projectors/ArrowValueProjectorImpl.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/projectors/ArrowValueProjectorImpl.java new file mode 100644 index 0000000000..4bfac587d5 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/projectors/ArrowValueProjectorImpl.java @@ -0,0 +1,133 @@ +package com.amazonaws.athena.connector.lambda.data.projectors; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.BlockUtils; +import org.apache.arrow.vector.complex.reader.FieldReader; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.util.Text; + +import java.time.Instant; +import java.time.LocalDate; +import java.util.Objects; + +/** + * Abstract class that shares common logic to create the {@link ArrowValueProjectorImpl.Projection Projection} instance. + * {@link ArrowValueProjectorImpl.Projection}'s implementation is decided at runtime based on + * input {@link Types.MinorType Arrow minor type}. + */ +public abstract class ArrowValueProjectorImpl + implements ArrowValueProjector +{ + /** + * Concrete implementation of ArrowValueProjectorImpl should invoke thie method to get the Projection instance. + * @param minorType + * @return Projection used by child class to do actual projection work. + */ + protected Projection createValueProjection(Types.MinorType minorType) + { + switch (minorType) { + case LIST: + case STRUCT: + return createComplexValueProjection(minorType); + default: + return createSimpleValueProjection(minorType); + } + } + + private Projection createSimpleValueProjection(Types.MinorType minorType) + { + switch (minorType) { + case DATEMILLI: + return (fieldReader) -> { + if (Objects.isNull(fieldReader.readLocalDateTime())) { + return null; + } + long millis = fieldReader.readLocalDateTime().toDateTime(org.joda.time.DateTimeZone.UTC).getMillis(); + return Instant.ofEpochMilli(millis).atZone(BlockUtils.UTC_ZONE_ID).toLocalDateTime(); + }; + case TINYINT: + case UINT1: + return (fieldReader) -> fieldReader.readByte(); + case UINT2: + return (fieldReader) -> fieldReader.readCharacter(); + case SMALLINT: + return (fieldReader) -> fieldReader.readShort(); + case DATEDAY: + return (fieldReader) -> { + Integer intVal = fieldReader.readInteger(); + if (Objects.isNull(intVal)) { + return null; + } + return LocalDate.ofEpochDay(intVal); + }; + case INT: + case UINT4: + return (fieldReader) -> fieldReader.readInteger(); + case UINT8: + case BIGINT: + return (fieldReader) -> fieldReader.readLong(); + case DECIMAL: + return (fieldReader) -> fieldReader.readBigDecimal(); + case FLOAT4: + return (fieldReader) -> fieldReader.readFloat(); + case FLOAT8: + return (fieldReader) -> fieldReader.readDouble(); + case VARCHAR: + return (fieldReader) -> { + Text text = fieldReader.readText(); + if (Objects.isNull(text)) { + return null; + } + return text.toString(); + }; + case VARBINARY: + return (fieldReader) -> fieldReader.readByteArray(); + case BIT: + return (fieldReader) -> fieldReader.readBoolean(); + default: + throw new IllegalArgumentException("Unsupported type " + minorType); + } + } + + private Projection createComplexValueProjection(Types.MinorType minorType) + { + switch (minorType) { + case LIST: + return (fieldReader) -> { + ListArrowValueProjector subListProjector = new ListArrowValueProjector(fieldReader); + return subListProjector.doProject(); + }; + case STRUCT: + return (fieldReader) -> { + StructArrowValueProjector subStructProjector = new StructArrowValueProjector(fieldReader); + return subStructProjector.doProject(); + }; + default: + throw new IllegalArgumentException("Unsupported type " + minorType); + } + } + + interface Projection + { + Object doProjection(FieldReader fieldReader); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/projectors/ListArrowValueProjector.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/projectors/ListArrowValueProjector.java new file mode 100644 index 0000000000..ba788598a1 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/projectors/ListArrowValueProjector.java @@ -0,0 +1,78 @@ +package com.amazonaws.athena.connector.lambda.data.projectors; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import org.apache.arrow.vector.complex.reader.FieldReader; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Field; + +import java.util.ArrayList; +import java.util.List; + +import static java.util.Objects.requireNonNull; + +public class ListArrowValueProjector + extends ArrowValueProjectorImpl +{ + private final FieldReader listReader; + private final Projection projection; + + public ListArrowValueProjector(FieldReader listReader) + { + this.listReader = requireNonNull(listReader, "listReader is null"); + + List children = listReader.getField().getChildren(); + if (children.size() != 1) { + throw new RuntimeException("Unexpected number of children for ListProjector field " + + listReader.getField().getName()); + } + Types.MinorType minorType = Types.getMinorTypeForArrowType(children.get(0).getType()); + projection = createValueProjection(minorType); + } + + @Override + public Object project(int pos) + { + listReader.setPosition(pos); + if (!listReader.isSet()) { + return null; + } + + return doProject(); + } + + protected Object doProject() + { + List list = new ArrayList<>(); + + while (listReader.next()) { + FieldReader subReader = listReader.reader(); // same reader with different idx + if (!subReader.isSet()) { + list.add(null); + continue; + } + + Object value = projection.doProjection(subReader); + list.add(value); + } + return list; + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/projectors/ProjectorUtils.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/projectors/ProjectorUtils.java new file mode 100644 index 0000000000..391f3fd94a --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/projectors/ProjectorUtils.java @@ -0,0 +1,42 @@ +package com.amazonaws.athena.connector.lambda.data.projectors; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import org.apache.arrow.vector.complex.reader.FieldReader; + +public class ProjectorUtils +{ + private ProjectorUtils() + { + } + + public static ArrowValueProjector createArrowValueProjector(FieldReader fieldReader) + { + switch (fieldReader.getMinorType()) { + case LIST: + return new ListArrowValueProjector(fieldReader); + case STRUCT: + return new StructArrowValueProjector(fieldReader); + default: + return new SimpleArrowValueProjector(fieldReader); + } + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/projectors/SimpleArrowValueProjector.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/projectors/SimpleArrowValueProjector.java new file mode 100644 index 0000000000..91f74e193d --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/projectors/SimpleArrowValueProjector.java @@ -0,0 +1,45 @@ +package com.amazonaws.athena.connector.lambda.data.projectors; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import org.apache.arrow.vector.complex.reader.FieldReader; + +import static java.util.Objects.requireNonNull; + +public class SimpleArrowValueProjector + extends ArrowValueProjectorImpl +{ + private final Projection projection; + private final FieldReader fieldReader; + + public SimpleArrowValueProjector(FieldReader fieldReader) + { + this.fieldReader = requireNonNull(fieldReader, "fieldReader is null"); + this.projection = createValueProjection(fieldReader.getMinorType()); + } + + @Override + public Object project(int pos) + { + fieldReader.setPosition(pos); + return projection.doProjection(fieldReader); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/projectors/StructArrowValueProjector.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/projectors/StructArrowValueProjector.java new file mode 100644 index 0000000000..3374ada54e --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/projectors/StructArrowValueProjector.java @@ -0,0 +1,81 @@ +package com.amazonaws.athena.connector.lambda.data.projectors; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.google.common.collect.ImmutableMap; +import org.apache.arrow.vector.complex.reader.FieldReader; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Field; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static java.util.Objects.requireNonNull; + +public class StructArrowValueProjector + extends ArrowValueProjectorImpl +{ + private final Map projectionsMap; + private final FieldReader structReader; + + public StructArrowValueProjector(FieldReader structReader) + { + this.structReader = requireNonNull(structReader, "structReader is null"); + + ImmutableMap.Builder projectionMapBuilder = ImmutableMap.builder(); + + List children = structReader.getField().getChildren(); + + for (Field child : children) { + String childName = child.getName(); + Types.MinorType minorType = Types.getMinorTypeForArrowType(child.getType()); + Projection projection = createValueProjection(minorType); + projectionMapBuilder.put(childName, projection); + } + + this.projectionsMap = projectionMapBuilder.build(); + } + + @Override + public Object project(int pos) + { + structReader.setPosition(pos); + if (!structReader.isSet()) { + return null; + } + + return doProject(); + } + + protected Map doProject() + { + List fields = structReader.getField().getChildren(); + Map nameToValues = new HashMap<>(); + for (Field child : fields) { + String childName = child.getName(); + FieldReader subReader = structReader.reader(childName); + Projection childProjection = projectionsMap.get(childName); + nameToValues.put(child.getName(), childProjection.doProjection(subReader)); + } + return nameToValues; + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/writers/ArrowValueWriter.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/writers/ArrowValueWriter.java new file mode 100644 index 0000000000..e368ccd076 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/writers/ArrowValueWriter.java @@ -0,0 +1,36 @@ +package com.amazonaws.athena.connector.lambda.data.writers; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +/** + * Implementation of this interface is expected to project java values into Arrow vectors. The implementation is + * expected to take into a vector during initialization. Each call of {@link #write(int, Object) project} would + * write one Java value into the Arrow vector. + */ +public interface ArrowValueWriter +{ + /** + * Project the java value into Arrow's vector. + * @param pos the position/row to project to + * @param value the original java value to be projected + */ + void write(int pos, Object value); +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/writers/ComplexArrowValueWriter.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/writers/ComplexArrowValueWriter.java new file mode 100644 index 0000000000..3a4776269c --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/writers/ComplexArrowValueWriter.java @@ -0,0 +1,47 @@ +package com.amazonaws.athena.connector.lambda.data.writers; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.BlockUtils; +import com.amazonaws.athena.connector.lambda.data.FieldResolver; +import org.apache.arrow.vector.FieldVector; + +import static java.util.Objects.requireNonNull; + +public class ComplexArrowValueWriter + implements ArrowValueWriter +{ + private final FieldResolver resolver; + private final FieldVector fieldVector; + + public ComplexArrowValueWriter(FieldVector fieldVector, FieldResolver resolver) + { + this.resolver = requireNonNull(resolver, "resolver is null"); + this.fieldVector = requireNonNull(fieldVector, "fieldVector is null"); + } + + @Override + public void write(int pos, Object value) + { + //todo: use projection pattern + BlockUtils.setComplexValue(fieldVector, pos, resolver, value); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/writers/SimpleArrowValueWriter.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/writers/SimpleArrowValueWriter.java new file mode 100644 index 0000000000..73d46748bd --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/writers/SimpleArrowValueWriter.java @@ -0,0 +1,312 @@ +package com.amazonaws.athena.connector.lambda.data.writers; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.BlockUtils; +import org.apache.arrow.vector.BigIntVector; +import org.apache.arrow.vector.BitVector; +import org.apache.arrow.vector.DateDayVector; +import org.apache.arrow.vector.DateMilliVector; +import org.apache.arrow.vector.DecimalVector; +import org.apache.arrow.vector.FieldVector; +import org.apache.arrow.vector.Float4Vector; +import org.apache.arrow.vector.Float8Vector; +import org.apache.arrow.vector.IntVector; +import org.apache.arrow.vector.SmallIntVector; +import org.apache.arrow.vector.TinyIntVector; +import org.apache.arrow.vector.UInt1Vector; +import org.apache.arrow.vector.UInt2Vector; +import org.apache.arrow.vector.UInt4Vector; +import org.apache.arrow.vector.UInt8Vector; +import org.apache.arrow.vector.VarBinaryVector; +import org.apache.arrow.vector.VarCharVector; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.util.Text; +import org.apache.commons.codec.Charsets; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Date; + +import static com.amazonaws.athena.connector.lambda.data.BlockUtils.UTC_ZONE_ID; +import static java.util.Objects.requireNonNull; + +public class SimpleArrowValueWriter + implements ArrowValueWriter +{ + private final Writer writer; + private final FieldVector fieldVector; + + public SimpleArrowValueWriter(FieldVector fieldVector) + { + this.fieldVector = requireNonNull(fieldVector, "fieldVector is null"); + this.writer = createWriter(fieldVector.getMinorType()); + } + + private Writer createWriter(Types.MinorType minorType) + { + switch (minorType) { + case DATEMILLI: + return (vector, pos, value) -> { + DateMilliVector dateMilliVector = ((DateMilliVector) vector); + if (value == null) { + dateMilliVector.setNull(pos); + return; + } + + if (value instanceof Date) { + dateMilliVector.setSafe(pos, ((Date) value).getTime()); + } + else if (value instanceof LocalDateTime) { + dateMilliVector.setSafe( + pos, + ((LocalDateTime) value).atZone(UTC_ZONE_ID).toInstant().toEpochMilli()); + } + else { + dateMilliVector.setSafe(pos, (long) value); + } + }; + case DATEDAY: + return (vector, pos, value) -> { + DateDayVector dateDayVector = ((DateDayVector) vector); + if (value == null) { + dateDayVector.setNull(pos); + return; + } + + if (value instanceof Date) { + org.joda.time.Days days = org.joda.time.Days.daysBetween(BlockUtils.EPOCH, + new org.joda.time.DateTime(((Date) value).getTime())); + dateDayVector.setSafe(pos, days.getDays()); + } + if (value instanceof LocalDate) { + int days = (int) ((LocalDate) value).toEpochDay(); + dateDayVector.setSafe(pos, days); + } + else if (value instanceof Long) { + dateDayVector.setSafe(pos, ((Long) value).intValue()); + } + else { + dateDayVector.setSafe(pos, (int) value); + } + }; + case FLOAT8: + return (vector, pos, value) -> { + Float8Vector float8Vector = ((Float8Vector) vector); + if (value == null) { + float8Vector.setNull(pos); + return; + } + + float8Vector.setSafe(pos, (double) value); + }; + case FLOAT4: + return (vector, pos, value) -> { + Float4Vector float4Vector = ((Float4Vector) vector); + if (value == null) { + float4Vector.setNull(pos); + return; + } + + ((Float4Vector) vector).setSafe(pos, (float) value); + }; + case INT: + return (vector, pos, value) -> { + IntVector intVector = ((IntVector) vector); + if (value == null) { + intVector.setNull(pos); + return; + } + + if (value != null && value instanceof Long) { + //This may seem odd at first but many frameworks (like Presto) use long as the preferred + //native java type for representing integers. We do this to keep type conversions simple. + intVector.setSafe(pos, ((Long) value).intValue()); + } + else { + intVector.setSafe(pos, (int) value); + } + }; + case TINYINT: + return (vector, pos, value) -> { + TinyIntVector tinyIntVector = ((TinyIntVector) vector); + if (value == null) { + tinyIntVector.setNull(pos); + return; + } + + if (value instanceof Byte) { + tinyIntVector.setSafe(pos, (byte) value); + } + else { + tinyIntVector.setSafe(pos, (int) value); + } + }; + case SMALLINT: + return (vector, pos, value) -> { + SmallIntVector smallIntVector = ((SmallIntVector) vector); + if (value == null) { + smallIntVector.setNull(pos); + return; + } + + if (value instanceof Short) { + smallIntVector.setSafe(pos, (short) value); + } + else { + smallIntVector.setSafe(pos, (int) value); + } + }; + case UINT1: + return (vector, pos, value) -> { + UInt1Vector uInt1Vector = ((UInt1Vector) vector); + if (value == null) { + uInt1Vector.setNull(pos); + return; + } + + ((UInt1Vector) vector).setSafe(pos, (int) value); + }; + case UINT2: + return (vector, pos, value) -> { + UInt2Vector uInt2Vector = ((UInt2Vector) vector); + if (value == null) { + uInt2Vector.setNull(pos); + return; + } + + ((UInt2Vector) vector).setSafe(pos, (int) value); + }; + case UINT4: + return (vector, pos, value) -> { + UInt4Vector uInt4Vector = ((UInt4Vector) vector); + if (value == null) { + uInt4Vector.setNull(pos); + return; + } + + ((UInt4Vector) vector).setSafe(pos, (int) value); + }; + case UINT8: + return (vector, pos, value) -> { + UInt8Vector uInt8Vector = ((UInt8Vector) vector); + if (value == null) { + uInt8Vector.setNull(pos); + return; + } + + ((UInt8Vector) vector).setSafe(pos, (int) value); + }; + case BIGINT: + return (vector, pos, value) -> { + BigIntVector bigIntVector = ((BigIntVector) vector); + if (value == null) { + bigIntVector.setNull(pos); + return; + } + + ((BigIntVector) vector).setSafe(pos, (long) value); + }; + case VARBINARY: + return (vector, pos, value) -> { + VarBinaryVector varBinaryVector = ((VarBinaryVector) vector); + if (value == null) { + varBinaryVector.setNull(pos); + return; + } + + ((VarBinaryVector) vector).setSafe(pos, (byte[]) value); + }; + case DECIMAL: + return (vector, pos, value) -> { + DecimalVector dVector = ((DecimalVector) vector); + if (value == null) { + dVector.setNull(pos); + return; + } + + if (value instanceof Double) { + BigDecimal bdVal = new BigDecimal((double) value); + bdVal = bdVal.setScale(dVector.getScale(), RoundingMode.HALF_UP); + dVector.setSafe(pos, bdVal); + } + else { + BigDecimal scaledValue = ((BigDecimal) value).setScale(dVector.getScale(), RoundingMode.HALF_UP); + ((DecimalVector) vector).setSafe(pos, scaledValue); + } + }; + case VARCHAR: + return (vector, pos, value) -> { + VarCharVector varCharVector = ((VarCharVector) vector); + if (value == null) { + varCharVector.setNull(pos); + return; + } + + if (value instanceof String) { + varCharVector.setSafe(pos, ((String) value).getBytes(Charsets.UTF_8)); + } + else { + varCharVector.setSafe(pos, (Text) value); + } + }; + case BIT: + return (vector, pos, value) -> { + BitVector bitVector = ((BitVector) vector); + if (value == null) { + bitVector.setNull(pos); + return; + } + + if (value instanceof Integer && (int) value > 0) { + bitVector.setSafe(pos, 1); + } + else if (value instanceof Boolean && (boolean) value) { + bitVector.setSafe(pos, 1); + } + else { + bitVector.setSafe(pos, 0); + } + }; + default: + throw new IllegalArgumentException("Unknown type " + fieldVector.getMinorType()); + } + } + + @Override + public void write(int pos, Object value) + { + try { + writer.write(fieldVector, pos, value); + } + catch (RuntimeException ex) { + String fieldName = (fieldVector != null) ? fieldVector.getField().getName() : "null_vector"; + throw new RuntimeException("Unable to set value for field " + fieldName + " using value " + value, ex); + } + } + + interface Writer + { + void write(FieldVector vector, int pos, Object value); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/writers/WriterUtils.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/writers/WriterUtils.java new file mode 100644 index 0000000000..73cf5dbb2c --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/data/writers/WriterUtils.java @@ -0,0 +1,42 @@ +package com.amazonaws.athena.connector.lambda.data.writers; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.FieldResolver; +import org.apache.arrow.vector.FieldVector; + +public class WriterUtils +{ + private WriterUtils() + { + } + + public static ArrowValueWriter createArrowValueWriter(FieldVector fieldVector) + { + switch (fieldVector.getMinorType()) { + case LIST: + case STRUCT: + return new ComplexArrowValueWriter(fieldVector, FieldResolver.DEFAULT); + default: + return new SimpleArrowValueWriter(fieldVector); + } + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/Split.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/Split.java new file mode 100644 index 0000000000..e6f0925df9 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/Split.java @@ -0,0 +1,241 @@ +package com.amazonaws.athena.connector.lambda.domain; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.domain.spill.SpillLocation; +import com.amazonaws.athena.connector.lambda.security.EncryptionKey; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.beans.Transient; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import static java.util.Objects.requireNonNull; + +/** + * A Split is best thought of as a unit of work that is part of a larger activity. For example, if we needed to + * read a 100GB table stored in S3. We might want to improve performance by parallelizing the reads of this large file + * by _splitting_ it up into 100MB pieces. You could think of each piece as a split. In general, Splits are opaque + * to Athena with the exception of the SpillLocation and EncryptionKey which are used by Athena to find any data that + * was spilled by the processing of the split. All properties on the split are soley produced by and consumed by the + * connector. + */ +public class Split +{ + //The optional SpillLocation this Split can write to. + private final SpillLocation spillLocation; + //The optional EncryptionKey this Split can use to encrypt/decrypt data. + private final EncryptionKey encryptionKey; + //The properties that define what this split is meant to do. + private final Map properties; + + /** + * Basic constructor. + * + * @param spillLocation The optional SpillLocation this Split can write to. + * @param encryptionKey The optional EncryptionKey this Split can use to encrypt/decrypt data. + * @param properties The properties that define what this split is meant to do. + */ + @JsonCreator + public Split(@JsonProperty("spillLocation") SpillLocation spillLocation, + @JsonProperty("encryptionKey") EncryptionKey encryptionKey, + @JsonProperty("properties") Map properties) + { + requireNonNull(properties, "properties is null"); + this.spillLocation = spillLocation; + this.encryptionKey = encryptionKey; + this.properties = Collections.unmodifiableMap(properties); + } + + private Split(Builder builder) + { + this.properties = Collections.unmodifiableMap(builder.properties); + this.spillLocation = builder.spillLocation; + this.encryptionKey = builder.encryptionKey; + } + + /** + * Retrieves the value of the requested property. + * + * @param key The name of the property to retrieve. + * @return The value for that property or null if there is no such property. + */ + @Transient + public String getProperty(String key) + { + return properties.get(key); + } + + /** + * Retrieves the value of the requested property and attempts to parse the value into an int. + * + * @param key The name of the property to retrieve. + * @return The value for that property, throws if there is no such property. + */ + @Transient + public int getPropertyAsInt(String key) + { + return Integer.parseInt(properties.get(key)); + } + + /** + * Retrieves the value of the requested property and attempts to parse the value into an Long. + * + * @param key The name of the property to retrieve. + * @return The value for that property, throws if there is no such property. + */ + @Transient + public long getPropertyAsLong(String key) + { + return Long.parseLong(properties.get(key)); + } + + /** + * Retrieves the value of the requested property and attempts to parse the value into a double. + * + * @param key The name of the property to retrieve. + * @return The value for that property, throws if there is no such property. + */ + @Transient + public double getPropertyAsDouble(String key) + { + return Double.parseDouble(properties.get(key)); + } + + /** + * Provides access to all properties on this Split. + * + * @return Map containing all properties on the split. + */ + @JsonProperty + public Map getProperties() + { + return properties; + } + + /** + * The optional SpillLocation this Split can write to. + * + * @return The SpillLocation. + */ + @JsonProperty + public SpillLocation getSpillLocation() + { + return spillLocation; + } + + /** + * The optional EncryptionKey this Split can use to encrypt/decrypt data. + * + * @return The EncryptionKey. + */ + @JsonProperty + public EncryptionKey getEncryptionKey() + { + return encryptionKey; + } + + @Transient + public static Builder newBuilder(SpillLocation spillLocation, EncryptionKey encryptionKey) + { + return new Builder().withSpillLocation(spillLocation).withEncryptionKey(encryptionKey); + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Split split = (Split) o; + return Objects.equals(spillLocation, split.spillLocation) && + Objects.equals(encryptionKey, split.encryptionKey) && + Objects.equals(getProperties(), split.getProperties()); + } + + @Override + public int hashCode() + { + return Objects.hash(spillLocation, encryptionKey, getProperties()); + } + + public static class Builder + { + private final Map properties = new HashMap<>(); + private SpillLocation spillLocation; + private EncryptionKey encryptionKey; + + private Builder() {} + + /** + * Adds the provided property key,value pair to the Split, overwriting any previous value for the key. + * + * @param key The key for the property. + * @param value The value for the property. + * @return The Builder itself. + */ + public Builder add(String key, String value) + { + properties.put(key, value); + return this; + } + + /** + * Sets the optional SpillLocation this Split can write to. + * + * @param val The SpillLocation + * @return The Builder itself. + */ + public Builder withSpillLocation(SpillLocation val) + { + this.spillLocation = val; + return this; + } + + /** + * Sets the optional EncryptionKey this Split can use to encrypt/decrypt data. + * + * @param key The EncryptionKey + * @return The Builder itself. + */ + public Builder withEncryptionKey(EncryptionKey key) + { + this.encryptionKey = key; + return this; + } + + /** + * Builds the Split + * + * @return A newly constructed Split using the attributes collected by this builder. + */ + public Split build() + { + return new Split(this); + } + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/TableName.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/TableName.java new file mode 100644 index 0000000000..e74ce5d289 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/TableName.java @@ -0,0 +1,106 @@ +package com.amazonaws.athena.connector.lambda.domain; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.MoreObjects; +import com.google.common.base.Objects; + +import static java.util.Objects.requireNonNull; + +/** + * Represents a fully qualified TableName. + */ +public class TableName +{ + //The schema name that the table belongs to. + private final String schemaName; + //The name of the table. + private final String tableName; + + /** + * Constructs a fully qualified TableName. + * + * @param schemaName The name of the schema that the table belongs to. + * @param tableName The name of the table. + */ + @JsonCreator + public TableName(@JsonProperty("schemaName") String schemaName, + @JsonProperty("tableName") String tableName) + { + this.schemaName = requireNonNull(schemaName, "schemaName is null"); + this.tableName = requireNonNull(tableName, "tableName is null"); + } + + /** + * Gets the name of the schema the table belongs to. + * + * @return A String containing the schema name for the table. + */ + @JsonProperty + public String getSchemaName() + { + return schemaName; + } + + /** + * Gets the name of the table. + * + * @return A String containing the name of the table. + */ + @JsonProperty + public String getTableName() + { + return tableName; + } + + @Override + public String toString() + { + return MoreObjects.toStringHelper(this) + .add("schemaName", schemaName) + .add("tableName", tableName) + .toString(); + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + TableName that = (TableName) o; + + return Objects.equal(this.schemaName, that.schemaName) && + Objects.equal(this.tableName, that.tableName); + } + + @Override + public int hashCode() + { + return Objects.hashCode(schemaName, tableName); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/AllOrNoneValueSet.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/AllOrNoneValueSet.java new file mode 100644 index 0000000000..f794100e9e --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/AllOrNoneValueSet.java @@ -0,0 +1,202 @@ +package com.amazonaws.athena.connector.lambda.domain.predicate; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.apache.arrow.vector.types.pojo.ArrowType; + +import java.util.Objects; + +import static java.util.Objects.requireNonNull; + +/** + * Describes a constraint as a ValueSet which can have one of several states: + * 1. No value can match + * 2. Only NULL values can match + * 3. Only non-null values can match + * 4. All values can match + * + * @see ValueSet + */ +public class AllOrNoneValueSet + implements ValueSet +{ + private final ArrowType type; + private final boolean all; + private final boolean nullAllowed; + + @JsonCreator + public AllOrNoneValueSet(@JsonProperty("type") ArrowType type, + @JsonProperty("all") boolean all, + @JsonProperty("nullAllowed") boolean nullAllowed) + { + this.type = requireNonNull(type, "type is null"); + this.all = all; + this.nullAllowed = nullAllowed; + } + + static AllOrNoneValueSet all(ArrowType type) + { + return new AllOrNoneValueSet(type, true, true); + } + + static AllOrNoneValueSet none(ArrowType type) + { + return new AllOrNoneValueSet(type, false, false); + } + + static AllOrNoneValueSet onlyNull(ArrowType type) + { + return new AllOrNoneValueSet(type, false, true); + } + + static AllOrNoneValueSet notNull(ArrowType type) + { + return new AllOrNoneValueSet(type, true, false); + } + + @Override + @JsonProperty("nullAllowed") + public boolean isNullAllowed() + { + return nullAllowed; + } + + @Override + @JsonProperty + public ArrowType getType() + { + return type; + } + + @Override + public boolean isNone() + { + return !all && !nullAllowed; + } + + @Override + @JsonProperty + public boolean isAll() + { + return all && nullAllowed; + } + + @Override + public boolean isSingleValue() + { + return false; + } + + @Override + public Object getSingleValue() + { + throw new UnsupportedOperationException(); + } + + @Override + public boolean containsValue(Marker value) + { + if (value.isNullValue() && nullAllowed) { + return true; + } + else if (value.isNullValue() && !nullAllowed) { + return false; + } + + return all; + } + + @Override + public ValueSet intersect(BlockAllocator allocator, ValueSet other) + { + AllOrNoneValueSet otherValueSet = checkCompatibility(other); + return new AllOrNoneValueSet(type, all && otherValueSet.all, nullAllowed && other.isNullAllowed()); + } + + @Override + public ValueSet union(BlockAllocator allocator, ValueSet other) + { + AllOrNoneValueSet otherValueSet = checkCompatibility(other); + return new AllOrNoneValueSet(type, all || otherValueSet.all, nullAllowed || other.isNullAllowed()); + } + + @Override + public ValueSet complement(BlockAllocator allocator) + { + return new AllOrNoneValueSet(type, !all, !nullAllowed); + } + + @Override + public String toString() + { + return "[" + (all ? "ALL" : "NONE") + " nullAllowed:" + isNullAllowed() + "]"; + } + + @Override + public int hashCode() + { + return Objects.hash(type, all); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + final AllOrNoneValueSet other = (AllOrNoneValueSet) obj; + return Objects.equals(this.type, other.type) + && this.all == other.all; + } + + private AllOrNoneValueSet checkCompatibility(ValueSet other) + { + if (!getType().equals(other.getType())) { + throw new IllegalArgumentException(String.format("Mismatched types: %s vs %s", + getType(), other.getType())); + } + if (!(other instanceof AllOrNoneValueSet)) { + throw new IllegalArgumentException(String.format("ValueSet is not a AllOrNoneValueSet: %s", + other.getClass())); + } + return (AllOrNoneValueSet) other; + } + + @Override + public void close() + throws Exception + { + } + + private void checkTypeCompatibility(Marker marker) + { + if (!getType().equals(marker.getType())) { + throw new IllegalStateException(String.format("Marker of %s does not match SortedRangeSet of %s", + marker.getType(), getType())); + } + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/ConstraintEvaluator.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/ConstraintEvaluator.java new file mode 100644 index 0000000000..8cb487740d --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/ConstraintEvaluator.java @@ -0,0 +1,105 @@ +package com.amazonaws.athena.connector.lambda.domain.predicate; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.arrow.vector.types.pojo.Schema; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.Map; + +/** + * Used to apply predicates to values inside your connector. Ideally you would also be able to push + * constraints into your source system (e.g. RDBMS via SQL). For each value you'd like to write for a given row, + * you call the 'apply' function on this class and if the values for all columns in the row return 'true' that + * indicates that the row passes the constraints. + * + * After being used, ConstraintEvaluator instance must be closed to ensure no Apache Arrow resources used by + * Markers that it creates as part of evaluation are leaked. + * + * @note This abstraction works well for the associative predicates that are made available to your connector + * today but will likely require enhancement as we expose more sophisticated predicates (e.g. col1 + col2 < 100) + * in the future. Additionally, we do not support constraints on complex types are this time. + *

+ * For usage examples, please see the ExampleRecordHandler or connectors like athena-redis. + * + * TODO: We can improve the filtering performance of ConstraintEvaluator by refactoring how ValueSets works. + */ +public class ConstraintEvaluator + implements AutoCloseable +{ + private static final Logger logger = LoggerFactory.getLogger(ConstraintEvaluator.class); + + private final Constraints constraints; + + //Used to reduce the object overhead of constraints by sharing blocks across Markers. + //This is a byproduct of the way we are using Apache Arrow to hold Markers which are essentially + //a single value blocks. This factory allows us to represent a Marker (aka a single value) as + //a row in a shared block to improve memory and perf. + private final MarkerFactory markerFactory; + //Holds the type for each field. + private final Map typeMap = new HashMap<>(); + + public ConstraintEvaluator(BlockAllocator allocator, Schema schema, Constraints constraints) + { + this.constraints = constraints; + for (Field next : schema.getFields()) { + typeMap.put(next.getName(), next.getType()); + } + markerFactory = new MarkerFactory(allocator); + } + + public static ConstraintEvaluator emptyEvaluator() + { + return new ConstraintEvaluator(null, SchemaBuilder.newBuilder().build(), new Constraints(new HashMap<>())); + } + + public boolean apply(String fieldName, Object value) + { + try { + ValueSet constraint = constraints.getSummary().get(fieldName); + if (constraint != null && typeMap.get(fieldName) != null) { + try (Marker marker = markerFactory.createNullable(typeMap.get(fieldName), + value, + Marker.Bound.EXACTLY)) { + return constraint.containsValue(marker); + } + } + + return true; + } + catch (Exception ex) { + throw (ex instanceof RuntimeException) ? (RuntimeException) ex : new RuntimeException(ex); + } + } + + @Override + public void close() + throws Exception + { + markerFactory.close(); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/Constraints.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/Constraints.java new file mode 100644 index 0000000000..5b07363c69 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/Constraints.java @@ -0,0 +1,87 @@ +package com.amazonaws.athena.connector.lambda.domain.predicate; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.Objects; + +import java.util.Map; + +public class Constraints + implements AutoCloseable +{ + private Map summary; + + @JsonCreator + public Constraints(@JsonProperty("summary") Map summary) + { + this.summary = summary; + } + + public Map getSummary() + { + return summary; + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + Constraints that = (Constraints) o; + + return Objects.equal(this.summary, that.summary); + } + + @Override + public String toString() + { + return "Constraints{" + + "summary=" + summary + + '}'; + } + + @Override + public int hashCode() + { + return Objects.hashCode(summary); + } + + @Override + public void close() + throws Exception + { + for (ValueSet next : summary.values()) { + try { + next.close(); + } + catch (Exception ex) { + throw new RuntimeException(ex); + } + } + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/EquatableValueSet.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/EquatableValueSet.java new file mode 100644 index 0000000000..7448333b76 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/EquatableValueSet.java @@ -0,0 +1,458 @@ +package com.amazonaws.athena.connector.lambda.domain.predicate; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.ArrowTypeComparator; +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockUtils; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.apache.arrow.vector.FieldVector; +import org.apache.arrow.vector.complex.reader.FieldReader; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.arrow.vector.types.pojo.Schema; + +import java.beans.Transient; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +import static java.util.Objects.requireNonNull; + +/** + * A set containing values that are uniquely identifiable. + * Assumes an infinite number of possible values. The values may be collectively included (aka whitelist) + * or collectively excluded (aka !whitelist). + */ +public class EquatableValueSet + implements ValueSet +{ + private static final String DEFAULT_COLUMN = "col1"; + private final boolean whiteList; + private final Block valueBlock; + public final boolean nullAllowed; + + @JsonCreator + public EquatableValueSet( + @JsonProperty("valueBlock") Block valueBlock, + @JsonProperty("whiteList") boolean whiteList, + @JsonProperty("nullAllowed") boolean nullAllowed) + { + requireNonNull(valueBlock, "valueBlock is null"); + this.valueBlock = valueBlock; + this.whiteList = whiteList; + this.nullAllowed = nullAllowed; + } + + static EquatableValueSet none(BlockAllocator allocator, ArrowType type) + { + return new EquatableValueSet(BlockUtils.newEmptyBlock(allocator, DEFAULT_COLUMN, type), true, false); + } + + static EquatableValueSet all(BlockAllocator allocator, ArrowType type) + { + return new EquatableValueSet(BlockUtils.newEmptyBlock(allocator, DEFAULT_COLUMN, type), false, true); + } + + static EquatableValueSet onlyNull(BlockAllocator allocator, ArrowType type) + { + return new EquatableValueSet(BlockUtils.newEmptyBlock(allocator, DEFAULT_COLUMN, type), false, true); + } + + static EquatableValueSet notNull(BlockAllocator allocator, ArrowType type) + { + return new EquatableValueSet(BlockUtils.newEmptyBlock(allocator, DEFAULT_COLUMN, type), false, false); + } + + static EquatableValueSet of(BlockAllocator allocator, ArrowType type, Object... values) + { + return new EquatableValueSet(BlockUtils.newBlock(allocator, DEFAULT_COLUMN, type, values), true, false); + } + + static EquatableValueSet of(BlockAllocator allocator, ArrowType type, boolean nullAllowed, Collection values) + { + return new EquatableValueSet(BlockUtils.newBlock(allocator, DEFAULT_COLUMN, type, values), true, nullAllowed); + } + + @JsonProperty("nullAllowed") + @Override + public boolean isNullAllowed() + { + return nullAllowed; + } + + @Transient + public Schema getSchema() + { + return valueBlock.getSchema(); + } + + @JsonProperty + public Block getValueBlock() + { + return valueBlock; + } + + @Transient + @Override + public ArrowType getType() + { + return valueBlock.getFieldReader(DEFAULT_COLUMN).getField().getType(); + } + + @JsonProperty + public boolean isWhiteList() + { + return whiteList; + } + + public Block getValues() + { + return valueBlock; + } + + public Object getValue(int pos) + { + FieldReader reader = valueBlock.getFieldReader(DEFAULT_COLUMN); + reader.setPosition(pos); + return reader.readObject(); + } + + @Override + public boolean isNone() + { + return whiteList && valueBlock.getRowCount() == 0 && !nullAllowed; + } + + @Override + public boolean isAll() + { + return !whiteList && valueBlock.getRowCount() == 0 && nullAllowed; + } + + @Override + public boolean isSingleValue() + { + return (whiteList && valueBlock.getRowCount() == 1 && !nullAllowed) || + (whiteList && valueBlock.getRowCount() == 0 && nullAllowed); + } + + @Override + public Object getSingleValue() + { + if (!isSingleValue()) { + throw new IllegalStateException("EquatableValueSet does not have just a single value"); + } + + if (nullAllowed && valueBlock.getRowCount() == 0) { + return null; + } + + FieldReader reader = valueBlock.getFieldReader(DEFAULT_COLUMN); + reader.setPosition(0); + return reader.readObject(); + } + + @Override + public boolean containsValue(Marker marker) + { + if (marker.isNullValue() && nullAllowed) { + return true; + } + else if (marker.isNullValue() && !nullAllowed) { + return false; + } + + Object value = marker.getValue(); + boolean result = false; + FieldReader reader = valueBlock.getFieldReader(DEFAULT_COLUMN); + for (int i = 0; i < valueBlock.getRowCount() && !result; i++) { + reader.setPosition(i); + result = ArrowTypeComparator.compare(reader, value, reader.readObject()) == 0; + } + return whiteList == result; + } + + protected boolean containsValue(Object value) + { + if (value == null && nullAllowed) { + return true; + } + + boolean result = false; + FieldReader reader = valueBlock.getFieldReader(DEFAULT_COLUMN); + for (int i = 0; i < valueBlock.getRowCount() && !result; i++) { + reader.setPosition(i); + result = ArrowTypeComparator.compare(reader, value, reader.readObject()) == 0; + } + return whiteList == result; + } + + @Override + public EquatableValueSet intersect(BlockAllocator allocator, ValueSet other) + { + EquatableValueSet otherValueSet = checkCompatibility(other); + boolean intersectNullAllowed = this.isNullAllowed() && other.isNullAllowed(); + + if (whiteList && otherValueSet.isWhiteList()) { + return new EquatableValueSet(intersect(allocator, this, otherValueSet), true, intersectNullAllowed); + } + else if (whiteList) { + return new EquatableValueSet(subtract(allocator, this, otherValueSet), true, intersectNullAllowed); + } + else if (otherValueSet.isWhiteList()) { + return new EquatableValueSet(subtract(allocator, otherValueSet, this), true, intersectNullAllowed); + } + else { + return new EquatableValueSet(union(allocator, otherValueSet, this), false, intersectNullAllowed); + } + } + + @Override + public EquatableValueSet union(BlockAllocator allocator, ValueSet other) + { + EquatableValueSet otherValueSet = checkCompatibility(other); + boolean unionNullAllowed = this.isNullAllowed() || other.isNullAllowed(); + + if (whiteList && otherValueSet.isWhiteList()) { + return new EquatableValueSet(union(allocator, otherValueSet, this), true, unionNullAllowed); + } + else if (whiteList) { + return new EquatableValueSet(subtract(allocator, otherValueSet, this), false, unionNullAllowed); + } + else if (otherValueSet.isWhiteList()) { + return new EquatableValueSet(subtract(allocator, this, otherValueSet), false, unionNullAllowed); + } + else { + return new EquatableValueSet(intersect(allocator, otherValueSet, this), false, unionNullAllowed); + } + } + + @Override + public EquatableValueSet complement(BlockAllocator allocator) + { + return new EquatableValueSet(valueBlock, !whiteList, !nullAllowed); + } + + @Override + public String toString() + { + return "EquatableValueSet{" + + "whiteList=" + whiteList + + "nullAllowed=" + nullAllowed + + ", valueBlock=" + valueBlock + + '}'; + } + + private static Block intersect(BlockAllocator allocator, EquatableValueSet left, EquatableValueSet right) + { + Block resultBlock = BlockUtils.newEmptyBlock(allocator, DEFAULT_COLUMN, left.getType()); + FieldVector result = resultBlock.getFieldVector(DEFAULT_COLUMN); + + Block lhsBlock = left.getValues(); + + FieldReader lhs = lhsBlock.getFieldReader(DEFAULT_COLUMN); + + int count = 0; + for (int i = 0; i < lhsBlock.getRowCount(); i++) { + lhs.setPosition(i); + if (isPresent(lhs.readObject(), right.valueBlock)) { + BlockUtils.setValue(result, count++, lhs.readObject()); + } + } + resultBlock.setRowCount(count); + return resultBlock; + } + + private static Block union(BlockAllocator allocator, EquatableValueSet left, EquatableValueSet right) + { + Block resultBlock = BlockUtils.newEmptyBlock(allocator, DEFAULT_COLUMN, left.getType()); + FieldVector result = resultBlock.getFieldVector(DEFAULT_COLUMN); + + Block lhsBlock = left.getValues(); + FieldReader lhs = lhsBlock.getFieldReader(DEFAULT_COLUMN); + + int count = 0; + for (int i = 0; i < lhsBlock.getRowCount(); i++) { + lhs.setPosition(i); + BlockUtils.setValue(result, count++, lhs.readObject()); + } + + Block rhsBlock = right.getValues(); + FieldReader rhs = rhsBlock.getFieldReader(DEFAULT_COLUMN); + for (int i = 0; i < rhsBlock.getRowCount(); i++) { + rhs.setPosition(i); + if (!isPresent(rhs.readObject(), left.valueBlock)) { + BlockUtils.setValue(result, count++, rhs.readObject()); + } + } + + resultBlock.setRowCount(count); + return resultBlock; + } + + private static Block subtract(BlockAllocator allocator, EquatableValueSet left, EquatableValueSet right) + { + Block resultBlock = BlockUtils.newEmptyBlock(allocator, DEFAULT_COLUMN, left.getType()); + FieldVector result = resultBlock.getFieldVector(DEFAULT_COLUMN); + + Block lhsBlock = left.getValues(); + + FieldReader lhs = lhsBlock.getFieldReader(DEFAULT_COLUMN); + + int count = 0; + for (int i = 0; i < lhsBlock.getRowCount(); i++) { + lhs.setPosition(i); + if (!isPresent(lhs.readObject(), right.valueBlock)) { + BlockUtils.setValue(result, count++, lhs.readObject()); + } + } + resultBlock.setRowCount(count); + return resultBlock; + } + + private static boolean isPresent(Object lhs, Block right) + { + FieldReader rhs = right.getFieldReader(DEFAULT_COLUMN); + Types.MinorType type = rhs.getMinorType(); + for (int j = 0; j < right.getRowCount(); j++) { + rhs.setPosition(j); + if (ArrowTypeComparator.compare(rhs, lhs, rhs.readObject()) == 0) { + return true; + } + } + return false; + } + + private EquatableValueSet checkCompatibility(ValueSet other) + { + if (!getType().equals(other.getType())) { + throw new IllegalStateException(String.format("Mismatched types: %s vs %s", + getType(), other.getType())); + } + if (!(other instanceof EquatableValueSet)) { + throw new IllegalStateException(String.format("ValueSet is not a EquatableValueSet: %s", other.getClass())); + } + return (EquatableValueSet) other; + } + + @Override + public int hashCode() + { + return Objects.hash(getType(), whiteList, valueBlock, nullAllowed); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + final EquatableValueSet other = (EquatableValueSet) obj; + + if (this.getType() != null && other.getType() != null && Types.getMinorTypeForArrowType(this.getType()) == Types.getMinorTypeForArrowType(other.getType())) { + //some arrow types require checking the minor type only, like Decimal. + //We ignore any params though we may want to reconsider that in the future + } + else if (this.getType() != other.getType()) { + return false; + } + + if (this.whiteList != other.whiteList) { + return false; + } + + if (this.nullAllowed != other.nullAllowed) { + return false; + } + + if (this.valueBlock == null && other.valueBlock != null) { + return false; + } + + if (this.valueBlock != null && !this.valueBlock.equalsAsSet(other.valueBlock)) { + return false; + } + + return true; + } + + @Override + public void close() + throws Exception + { + valueBlock.close(); + } + + public static Builder newBuilder(BlockAllocator allocator, ArrowType type, boolean isWhiteList, boolean nullAllowed) + { + return new Builder(allocator, type, isWhiteList, nullAllowed); + } + + public static class Builder + { + private ArrowType type; + private boolean isWhiteList; + private boolean nullAllowed; + private List values = new ArrayList<>(); + private BlockAllocator allocator; + + Builder(BlockAllocator allocator, ArrowType type, boolean isWhiteList, boolean nullAllowed) + { + requireNonNull(type, "minorType is null"); + this.allocator = allocator; + this.type = type; + this.isWhiteList = isWhiteList; + this.nullAllowed = nullAllowed; + } + + public Builder add(Object value) + { + values.add(value); + return this; + } + + public Builder addAll(Collection value) + { + values.addAll(value); + return this; + } + + public EquatableValueSet build() + { + return new EquatableValueSet(BlockUtils.newBlock(allocator, DEFAULT_COLUMN, type, values), isWhiteList, nullAllowed); + } + } + + private void checkTypeCompatibility(Marker marker) + { + if (!getType().equals(marker.getType())) { + throw new IllegalStateException(String.format("Marker of %s does not match SortedRangeSet of %s", + marker.getType(), getType())); + } + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/Marker.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/Marker.java new file mode 100644 index 0000000000..3b6acab61a --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/Marker.java @@ -0,0 +1,368 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.amazonaws.athena.connector.lambda.domain.predicate; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.ArrowTypeComparator; +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockUtils; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.MoreObjects; +import org.apache.arrow.vector.complex.reader.FieldReader; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.arrow.vector.types.pojo.Schema; + +import java.beans.Transient; + +import static java.util.Objects.requireNonNull; + +/** + * A point on the continuous space defined by the specified type. + * Each point may be just below, exact, or just above the specified value according to the Bound. + */ +public class Marker + implements Comparable, AutoCloseable +{ + protected static final String DEFAULT_COLUMN = "col1"; + + public enum Bound + { + BELOW, // lower than the value, but infinitesimally close to the value + EXACTLY, // exactly the value + ABOVE // higher than the value, but infinitesimally close to the value + } + + private final int valuePosition; + private final Block valueBlock; + private final Bound bound; + private final boolean nullValue; + + /** + * LOWER UNBOUNDED is specified with an empty value and a ABOVE bound + * UPPER UNBOUNDED is specified with an empty value and a BELOW bound + */ + @JsonCreator + public Marker( + @JsonProperty("valueBlock") Block valueBlock, + @JsonProperty("bound") Bound bound, + @JsonProperty("nullValue") boolean nullValue) + { + requireNonNull(valueBlock, "valueBlock is null"); + requireNonNull(bound, "bound is null"); + + this.valueBlock = valueBlock; + this.bound = bound; + this.nullValue = nullValue; + this.valuePosition = 0; + } + + protected Marker(Block block, + int valuePosition, + Bound bound, + boolean nullValue) + { + requireNonNull(block, "block is null"); + requireNonNull(bound, "bound is null"); + + this.valueBlock = block; + this.bound = bound; + this.nullValue = nullValue; + this.valuePosition = valuePosition; + } + + public boolean isNullValue() + { + return nullValue; + } + + @Transient + public ArrowType getType() + { + return valueBlock.getFieldReader(DEFAULT_COLUMN).getField().getType(); + } + + @Transient + public Object getValue() + { + if (nullValue) { + throw new IllegalStateException("No value to get"); + } + + FieldReader reader = valueBlock.getFieldReader(DEFAULT_COLUMN); + reader.setPosition(valuePosition); + return reader.readObject(); + } + + @JsonProperty + public Bound getBound() + { + return bound; + } + + @Transient + public Schema getSchema() + { + return valueBlock.getSchema(); + } + + @JsonProperty + public Block getValueBlock() + { + if (valueBlock.getRowCount() > 1) { + throw new RuntimeException("Attempting to get batch for a marker that appears to have a shared block"); + } + return valueBlock; + } + + @Transient + public boolean isUpperUnbounded() + { + return nullValue && bound == Bound.BELOW; + } + + @Transient + public boolean isLowerUnbounded() + { + return nullValue && bound == Bound.ABOVE; + } + + /** + * Adjacency is defined by two Markers being infinitesimally close to each other. + * This means they must share the same value and have adjacent Bounds. + */ + @Transient + public boolean isAdjacent(Marker other) + { + checkTypeCompatibility(other); + if (isUpperUnbounded() || isLowerUnbounded() || other.isUpperUnbounded() || other.isLowerUnbounded()) { + return false; + } + + if (ArrowTypeComparator.compare(getType(), getValue(), other.getValue()) != 0) { + return false; + } + + return (bound == Bound.EXACTLY && other.bound != Bound.EXACTLY) || + (bound != Bound.EXACTLY && other.bound == Bound.EXACTLY); + } + + public Marker greaterAdjacent() + { + if (nullValue) { + throw new IllegalStateException("No marker adjacent to unbounded"); + } + switch (bound) { + case BELOW: + return new Marker(valueBlock, valuePosition, Bound.EXACTLY, nullValue); + case EXACTLY: + return new Marker(valueBlock, valuePosition, Bound.ABOVE, nullValue); + case ABOVE: + throw new IllegalStateException("No greater marker adjacent to an ABOVE bound"); + default: + throw new AssertionError("Unsupported type: " + bound); + } + } + + public Marker lesserAdjacent() + { + if (nullValue) { + throw new IllegalStateException("No marker adjacent to unbounded"); + } + switch (bound) { + case BELOW: + throw new IllegalStateException("No lesser marker adjacent to a BELOW bound"); + case EXACTLY: + return new Marker(valueBlock, valuePosition, Bound.BELOW, nullValue); + case ABOVE: + return new Marker(valueBlock, valuePosition, Bound.EXACTLY, nullValue); + default: + throw new AssertionError("Unsupported type: " + bound); + } + } + + private void checkTypeCompatibility(Marker marker) + { + if (!getType().equals(marker.getType())) { + throw new IllegalArgumentException(String.format("Mismatched Marker types: %s vs %s", getType(), marker.getType())); + } + } + + public int compareTo(Marker o) + { + checkTypeCompatibility(o); + if (isUpperUnbounded()) { + return o.isUpperUnbounded() ? 0 : 1; + } + if (isLowerUnbounded()) { + return o.isLowerUnbounded() ? 0 : -1; + } + if (o.isUpperUnbounded()) { + return -1; + } + if (o.isLowerUnbounded()) { + return 1; + } + + // INVARIANT: value and o.value are present + if (valueBlock.getRowCount() < 1 || o.valueBlock.getRowCount() < 1) { + return Integer.compare(valueBlock.getRowCount(), o.valueBlock.getRowCount()); + } + + int compare = ArrowTypeComparator.compare(getType(), getValue(), o.getValue()); + if (compare == 0) { + if (bound == o.bound) { + return 0; + } + if (bound == Bound.BELOW) { + return -1; + } + if (bound == Bound.ABOVE) { + return 1; + } + // INVARIANT: bound == EXACTLY + return (o.bound == Bound.BELOW) ? 1 : -1; + } + return compare; + } + + public static Marker min(Marker marker1, Marker marker2) + { + return marker1.compareTo(marker2) <= 0 ? marker1 : marker2; + } + + public static Marker max(Marker marker1, Marker marker2) + { + return marker1.compareTo(marker2) >= 0 ? marker1 : marker2; + } + + private static Marker create(BlockAllocator allocator, ArrowType type, Object value, Bound bound) + { + return new Marker(BlockUtils.newBlock(allocator, Marker.DEFAULT_COLUMN, type, value), 0, bound, false); + } + + private static Marker create(BlockAllocator allocator, ArrowType type, Bound bound) + { + return new Marker(BlockUtils.newEmptyBlock(allocator, Marker.DEFAULT_COLUMN, type), 0, bound, true); + } + + public static Marker upperUnbounded(BlockAllocator allocator, ArrowType type) + { + requireNonNull(type, "type is null"); + return create(allocator, type, Bound.BELOW); + } + + public static Marker lowerUnbounded(BlockAllocator allocator, ArrowType type) + { + requireNonNull(type, "type is null"); + return create(allocator, type, Bound.ABOVE); + } + + public static Marker above(BlockAllocator allocator, ArrowType type, Object value) + { + requireNonNull(type, "type is null"); + requireNonNull(value, "value is null"); + return create(allocator, type, value, Bound.ABOVE); + } + + public static Marker exactly(BlockAllocator allocator, ArrowType type, Object value) + { + requireNonNull(type, "type is null"); + requireNonNull(value, "value is null"); + return create(allocator, type, value, Bound.EXACTLY); + } + + public static Marker nullMarker(BlockAllocator allocator, ArrowType type) + { + return create(allocator, type, Bound.EXACTLY); + } + + public static Marker below(BlockAllocator allocator, ArrowType type, Object value) + { + requireNonNull(type, "type is null"); + requireNonNull(value, "value is null"); + return create(allocator, type, value, Bound.BELOW); + } + + @Override + public int hashCode() + { + if (nullValue) { + return com.google.common.base.Objects.hashCode(nullValue, getType(), bound); + } + + return com.google.common.base.Objects.hashCode(nullValue, getType(), getValue(), bound); + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + Marker that = (Marker) o; + + boolean result = com.google.common.base.Objects.equal(nullValue, that.nullValue) && + com.google.common.base.Objects.equal(this.getType(), that.getType()) && + com.google.common.base.Objects.equal(this.bound, that.bound); + + if (result && !nullValue) { + result = com.google.common.base.Objects.equal(this.getValue(), that.getValue()); + } + + return result; + } + + @Override + public String toString() + { + return MoreObjects.toStringHelper(this) + .add("valueBlock", getType()) + .add("nullValue", nullValue) + .add("valueBlock", nullValue ? nullValue : getValue()) + .add("bound", bound) + .toString(); + } + + @Override + public void close() + throws Exception + { + valueBlock.close(); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/MarkerFactory.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/MarkerFactory.java new file mode 100644 index 0000000000..68eea57133 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/MarkerFactory.java @@ -0,0 +1,151 @@ +package com.amazonaws.athena.connector.lambda.domain.predicate; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockUtils; +import org.apache.arrow.vector.types.pojo.ArrowType; + +import java.util.HashMap; +import java.util.Map; + +import static com.amazonaws.athena.connector.lambda.data.BlockUtils.setValue; + +public class MarkerFactory + implements AutoCloseable +{ + private final BlockAllocator allocator; + private final Map sharedMarkerBlocks = new HashMap<>(); + private final Map markerLeases = new HashMap<>(); + + public MarkerFactory(BlockAllocator allocator) + { + this.allocator = allocator; + } + + public Marker createNullable(ArrowType type, Object value, Marker.Bound bound) + { + BlockLease lease = getOrCreateBlock(type); + if (value != null) { + setValue(lease.getBlock().getFieldVector(Marker.DEFAULT_COLUMN), lease.getPos(), value); + } + return new SharedBlockMarker(this, lease.getBlock(), lease.getPos(), bound, value == null); + } + + public Marker create(ArrowType type, Object value, Marker.Bound bound) + { + BlockLease lease = getOrCreateBlock(type); + setValue(lease.getBlock().getFieldVector(Marker.DEFAULT_COLUMN), lease.getPos(), value); + return new SharedBlockMarker(this, lease.getBlock(), lease.getPos(), bound, false); + } + + public Marker create(ArrowType type, Marker.Bound bound) + { + BlockLease lease = getOrCreateBlock(type); + return new SharedBlockMarker(this, lease.getBlock(), lease.getPos(), bound, true); + } + + private synchronized BlockLease getOrCreateBlock(ArrowType type) + { + Block sharedBlock = sharedMarkerBlocks.get(type); + Integer leaseNumber = markerLeases.get(type); + if (sharedBlock == null) { + sharedBlock = BlockUtils.newEmptyBlock(allocator, Marker.DEFAULT_COLUMN, type); + sharedMarkerBlocks.put(type, sharedBlock); + leaseNumber = 0; + } + markerLeases.put(type, ++leaseNumber); + BlockLease lease = new BlockLease(sharedBlock, leaseNumber - 1); + sharedBlock.setRowCount(leaseNumber); + return lease; + } + + /** + * This leasing strategy optimizes for the create, return usecase it does not attempt to handle fragmentation + * in any meaningful way beyond what the columnar nature of Arrow provides. + */ + private synchronized void returnBlockLease(ArrowType type, int pos) + { + Block sharedBlock = sharedMarkerBlocks.get(type); + Integer leaseNumber = markerLeases.get(type); + + if (sharedBlock != null && leaseNumber > 0 && leaseNumber == pos + 1) { + markerLeases.put(type, leaseNumber - 1); + } + } + + @Override + public void close() + throws Exception + { + for (Block next : sharedMarkerBlocks.values()) { + next.close(); + } + + sharedMarkerBlocks.clear(); + markerLeases.clear(); + } + + private static class BlockLease + { + private final Block block; + private final int pos; + + public BlockLease(Block block, int pos) + { + this.block = block; + this.pos = pos; + } + + public Block getBlock() + { + return block; + } + + public int getPos() + { + return pos; + } + } + + public class SharedBlockMarker + extends Marker + { + private final MarkerFactory factory; + private final int valuePosition; + + public SharedBlockMarker(MarkerFactory factory, Block block, int valuePosition, Bound bound, boolean nullValue) + { + super(block, valuePosition, bound, nullValue); + this.factory = factory; + this.valuePosition = valuePosition; + } + + @Override + public void close() + throws Exception + { + //Don't call close on the super since we don't own the block, it shared. + factory.returnBlockLease(getType(), valuePosition); + } + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/Range.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/Range.java new file mode 100644 index 0000000000..71170a0f35 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/Range.java @@ -0,0 +1,231 @@ +package com.amazonaws.athena.connector.lambda.domain.predicate; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.apache.arrow.vector.types.pojo.ArrowType; + +import java.beans.Transient; +import java.util.Objects; + +import static java.util.Objects.requireNonNull; + +public class Range + implements AutoCloseable +{ + private final Marker low; + private final Marker high; + + @JsonCreator + public Range( + @JsonProperty("low") Marker low, + @JsonProperty("high") Marker high) + { + requireNonNull(low, "low value is null"); + requireNonNull(high, "high value is null"); + if (!low.getType().equals(high.getType())) { + throw new IllegalArgumentException( + String.format("Marker types do not match: %s vs %s", low.getType(), high.getType())); + } + if (low.getBound() == Marker.Bound.BELOW) { + throw new IllegalArgumentException("low bound must be EXACTLY or ABOVE"); + } + if (high.getBound() == Marker.Bound.ABOVE) { + throw new IllegalArgumentException("high bound must be EXACTLY or BELOW"); + } + if (low.compareTo(high) > 0) { + throw new IllegalArgumentException("low must be less than or equal to high"); + } + this.low = low; + this.high = high; + } + + public static Range all(BlockAllocator allocator, ArrowType type) + { + return new Range(Marker.lowerUnbounded(allocator, type), Marker.upperUnbounded(allocator, type)); + } + + public static Range greaterThan(BlockAllocator allocator, ArrowType type, Object low) + { + return new Range(Marker.above(allocator, type, low), Marker.upperUnbounded(allocator, type)); + } + + public static Range greaterThanOrEqual(BlockAllocator allocator, ArrowType type, Object low) + { + return new Range(Marker.exactly(allocator, type, low), Marker.upperUnbounded(allocator, type)); + } + + public static Range lessThan(BlockAllocator allocator, ArrowType type, Object high) + { + return new Range(Marker.lowerUnbounded(allocator, type), Marker.below(allocator, type, high)); + } + + public static Range lessThanOrEqual(BlockAllocator allocator, ArrowType type, Object high) + { + return new Range(Marker.lowerUnbounded(allocator, type), Marker.exactly(allocator, type, high)); + } + + public static Range equal(BlockAllocator allocator, ArrowType type, Object value) + { + return new Range(Marker.exactly(allocator, type, value), Marker.exactly(allocator, type, value)); + } + + public static Range range(BlockAllocator allocator, ArrowType type, Object low, boolean lowInclusive, Object high, boolean highInclusive) + { + Marker lowMarker = lowInclusive ? Marker.exactly(allocator, type, low) : Marker.above(allocator, type, low); + Marker highMarker = highInclusive ? Marker.exactly(allocator, type, high) : Marker.below(allocator, type, high); + return new Range(lowMarker, highMarker); + } + + public ArrowType getType() + { + return low.getType(); + } + + @JsonProperty + public Marker getLow() + { + return low; + } + + @JsonProperty + public Marker getHigh() + { + return high; + } + + @Transient + public boolean isSingleValue() + { + return low.getBound() == Marker.Bound.EXACTLY && low.equals(high); + } + + @Transient + public Object getSingleValue() + { + if (!isSingleValue()) { + throw new IllegalStateException("Range does not have just a single value"); + } + return low.getValue(); + } + + @Transient + public boolean isAll() + { + return low.isLowerUnbounded() && high.isUpperUnbounded(); + } + + public boolean includes(Marker marker) + { + requireNonNull(marker, "marker is null"); + checkTypeCompatibility(marker); + return low.compareTo(marker) <= 0 && high.compareTo(marker) >= 0; + } + + public boolean contains(Range other) + { + checkTypeCompatibility(other); + return this.getLow().compareTo(other.getLow()) <= 0 && + this.getHigh().compareTo(other.getHigh()) >= 0; + } + + public Range span(Range other) + { + checkTypeCompatibility(other); + Marker lowMarker = Marker.min(low, other.getLow()); + Marker highMarker = Marker.max(high, other.getHigh()); + + return new Range(lowMarker, highMarker); + } + + public boolean overlaps(Range other) + { + checkTypeCompatibility(other); + return this.getLow().compareTo(other.getHigh()) <= 0 && + other.getLow().compareTo(this.getHigh()) <= 0; + } + + public Range intersect(Range other) + { + checkTypeCompatibility(other); + if (!this.overlaps(other)) { + throw new IllegalArgumentException("Cannot intersect non-overlapping ranges"); + } + Marker lowMarker = Marker.max(low, other.getLow()); + Marker highMarker = Marker.min(high, other.getHigh()); + return new Range(lowMarker, highMarker); + } + + private void checkTypeCompatibility(Range range) + { + if (!getType().equals(range.getType())) { + throw new IllegalArgumentException(String.format("Mismatched Range types: %s vs %s", + getType(), range.getType())); + } + } + + private void checkTypeCompatibility(Marker marker) + { + if (!getType().equals(marker.getType())) { + throw new IllegalArgumentException(String.format("Marker of %s does not match Range of %s", + marker.getType(), getType())); + } + } + + @Override + public int hashCode() + { + return Objects.hash(low, high); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + final Range other = (Range) obj; + return Objects.equals(this.low, other.low) && + Objects.equals(this.high, other.high); + } + + @Override + public String toString() + { + return com.google.common.base.MoreObjects.toStringHelper(this) + .add("low", low) + .add("high", high) + .toString(); + } + + @Override + public void close() + throws Exception + { + low.close(); + high.close(); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/Ranges.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/Ranges.java new file mode 100644 index 0000000000..ac5d15acc2 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/Ranges.java @@ -0,0 +1,38 @@ +package com.amazonaws.athena.connector.lambda.domain.predicate; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import java.util.List; + +public interface Ranges +{ + int getRangeCount(); + + /** + * @return Allowed non-overlapping predicate ranges sorted in increasing order + */ + List getOrderedRanges(); + + /** + * @return Single range encompassing all of allowed the ranges + */ + Range getSpan(); +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/SortedRangeSet.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/SortedRangeSet.java new file mode 100644 index 0000000000..9d0666e9e4 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/SortedRangeSet.java @@ -0,0 +1,505 @@ +package com.amazonaws.athena.connector.lambda.domain.predicate; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.apache.arrow.vector.types.pojo.ArrowType; + +import java.beans.Transient; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.NavigableMap; +import java.util.Objects; +import java.util.TreeMap; + +import static java.util.Objects.requireNonNull; + +public class SortedRangeSet + implements ValueSet +{ + private final boolean nullAllowed; + private final ArrowType type; + private final NavigableMap lowIndexedRanges; + + private SortedRangeSet(ArrowType type, NavigableMap lowIndexedRanges, boolean nullAllowed) + { + requireNonNull(type, "type is null"); + requireNonNull(lowIndexedRanges, "lowIndexedRanges is null"); + + this.type = type; + this.lowIndexedRanges = lowIndexedRanges; + this.nullAllowed = nullAllowed; + } + + static SortedRangeSet none(ArrowType type) + { + return copyOf(type, Collections.emptyList(), false); + } + + static SortedRangeSet all(BlockAllocator allocator, ArrowType type) + { + return copyOf(type, Collections.singletonList(Range.all(allocator, type)), true); + } + + static SortedRangeSet onlyNull(ArrowType type) + { + return copyOf(type, Collections.emptyList(), true); + } + + static SortedRangeSet notNull(BlockAllocator allocator, ArrowType type) + { + return copyOf(type, Collections.singletonList(Range.all(allocator, type)), false); + } + + static SortedRangeSet of(BlockAllocator allocator, ArrowType type, Object first, Object... rest) + { + return of(allocator, type, false, first, Arrays.asList(rest)); + } + + /** + * Provided discrete values that are unioned together to form the SortedRangeSet + */ + static SortedRangeSet of(BlockAllocator allocator, ArrowType type, boolean nullAllowed, Object first, Collection rest) + { + List ranges = new ArrayList<>(rest.size() + 1); + ranges.add(Range.equal(allocator, type, first)); + for (Object value : rest) { + ranges.add(Range.equal(allocator, type, value)); + } + return copyOf(type, ranges, nullAllowed); + } + + /** + * Provided Ranges are unioned together to form the SortedRangeSet + */ + public static SortedRangeSet of(Range first, Range... rest) + { + return of(false, first, Arrays.asList(rest)); + } + + /** + * Provided Ranges are unioned together to form the SortedRangeSet + */ + public static SortedRangeSet of(boolean nullAllowed, Range first, Range... rest) + { + return of(nullAllowed, first, Arrays.asList(rest)); + } + + /** + * Provided Ranges are unioned together to form the SortedRangeSet + */ + public static SortedRangeSet of(boolean nullAllowed, Range first, Collection rest) + { + List rangeList = new ArrayList<>(rest.size() + 1); + rangeList.add(first); + for (Range range : rest) { + rangeList.add(range); + } + return copyOf(first.getType(), rangeList, nullAllowed); + } + + /** + * Provided Ranges are unioned together to form the SortedRangeSet + */ + static SortedRangeSet copyOf(ArrowType type, Iterable ranges, boolean nullAllowed) + { + return new Builder(type, nullAllowed).addAll(ranges).build(); + } + + @JsonCreator + public static SortedRangeSet copyOf( + @JsonProperty("type") ArrowType type, + @JsonProperty("ranges") List ranges, + @JsonProperty("nullAllowed") boolean nullAllowed + ) + { + return copyOf(type, (Iterable) ranges, nullAllowed); + } + + @JsonProperty("nullAllowed") + @Override + public boolean isNullAllowed() + { + return nullAllowed; + } + + @JsonProperty + public ArrowType getType() + { + return type; + } + + @JsonProperty("ranges") + public List getOrderedRanges() + { + return new ArrayList<>(lowIndexedRanges.values()); + } + + @Transient + public int getRangeCount() + { + return lowIndexedRanges.size(); + } + + @Transient + @Override + public boolean isNone() + { + return lowIndexedRanges.isEmpty(); + } + + @Transient + @Override + public boolean isAll() + { + return lowIndexedRanges.size() == 1 && lowIndexedRanges.values().iterator().next().isAll(); + } + + @Transient + @Override + public boolean isSingleValue() + { + return (lowIndexedRanges.size() == 1 && lowIndexedRanges.values().iterator().next().isSingleValue() && !nullAllowed) || + lowIndexedRanges.isEmpty() && nullAllowed; + } + + @Transient + @Override + public Object getSingleValue() + { + if (!isSingleValue()) { + throw new IllegalStateException("SortedRangeSet does not have just a single value"); + } + + if (nullAllowed && lowIndexedRanges.isEmpty()) { + return null; + } + + return lowIndexedRanges.values().iterator().next().getSingleValue(); + } + + @Override + public boolean containsValue(Marker marker) + { + requireNonNull(marker, "marker is null"); + checkTypeCompatibility(marker); + + if (marker.isNullValue() && nullAllowed) { + return true; + } + else if (marker.isNullValue() && !nullAllowed) { + return false; + } + + if (marker.getBound() != Marker.Bound.EXACTLY) { + throw new RuntimeException("Expected Bound.EXACTLY but found " + marker.getBound()); + } + + Map.Entry floorEntry = lowIndexedRanges.floorEntry(marker); + return floorEntry != null && floorEntry.getValue().includes(marker); + } + + boolean includesMarker(Marker marker) + { + requireNonNull(marker, "marker is null"); + checkTypeCompatibility(marker); + + if (marker.isNullValue() && nullAllowed) { + return true; + } + + Map.Entry floorEntry = lowIndexedRanges.floorEntry(marker); + return floorEntry != null && floorEntry.getValue().includes(marker); + } + + @Transient + public Range getSpan() + { + if (lowIndexedRanges.isEmpty()) { + throw new IllegalStateException("Can not get span if no ranges exist"); + } + return lowIndexedRanges.firstEntry().getValue().span(lowIndexedRanges.lastEntry().getValue()); + } + + @Override + public Ranges getRanges() + { + return new Ranges() + { + @Override + public int getRangeCount() + { + return SortedRangeSet.this.getRangeCount(); + } + + @Override + public List getOrderedRanges() + { + return SortedRangeSet.this.getOrderedRanges(); + } + + @Override + public Range getSpan() + { + return SortedRangeSet.this.getSpan(); + } + }; + } + + @Override + public SortedRangeSet intersect(BlockAllocator allocator, ValueSet other) + { + SortedRangeSet otherRangeSet = checkCompatibility(other); + + boolean intersectNullAllowed = this.isNullAllowed() && other.isNullAllowed(); + Builder builder = new Builder(type, intersectNullAllowed); + + Iterator iterator1 = getOrderedRanges().iterator(); + Iterator iterator2 = otherRangeSet.getOrderedRanges().iterator(); + + if (iterator1.hasNext() && iterator2.hasNext()) { + Range range1 = iterator1.next(); + Range range2 = iterator2.next(); + + while (true) { + if (range1.overlaps(range2)) { + builder.add(range1.intersect(range2)); + } + + if (range1.getHigh().compareTo(range2.getHigh()) <= 0) { + if (!iterator1.hasNext()) { + break; + } + range1 = iterator1.next(); + } + else { + if (!iterator2.hasNext()) { + break; + } + range2 = iterator2.next(); + } + } + } + + return builder.build(); + } + + @Override + public SortedRangeSet union(BlockAllocator allocator, ValueSet other) + { + boolean unionNullAllowed = this.isNullAllowed() || other.isNullAllowed(); + SortedRangeSet otherRangeSet = checkCompatibility(other); + return new Builder(type, unionNullAllowed) + .addAll(this.getOrderedRanges()) + .addAll(otherRangeSet.getOrderedRanges()) + .build(); + } + + @Override + public SortedRangeSet union(BlockAllocator allocator, Collection valueSets) + { + boolean unionNullAllowed = this.isNullAllowed(); + for (ValueSet valueSet : valueSets) { + unionNullAllowed |= valueSet.isNullAllowed(); + } + + Builder builder = new Builder(type, unionNullAllowed); + builder.addAll(this.getOrderedRanges()); + for (ValueSet valueSet : valueSets) { + builder.addAll(checkCompatibility(valueSet).getOrderedRanges()); + } + return builder.build(); + } + + @Override + public SortedRangeSet complement(BlockAllocator allocator) + { + Builder builder = new Builder(type, !nullAllowed); + + if (lowIndexedRanges.isEmpty()) { + return builder.add(Range.all(allocator, type)).build(); + } + + Iterator rangeIterator = lowIndexedRanges.values().iterator(); + + Range firstRange = rangeIterator.next(); + if (!firstRange.getLow().isLowerUnbounded()) { + builder.add(new Range(Marker.lowerUnbounded(allocator, type), firstRange.getLow().lesserAdjacent())); + } + + Range previousRange = firstRange; + while (rangeIterator.hasNext()) { + Range currentRange = rangeIterator.next(); + + Marker lowMarker = previousRange.getHigh().greaterAdjacent(); + Marker highMarker = currentRange.getLow().lesserAdjacent(); + builder.add(new Range(lowMarker, highMarker)); + + previousRange = currentRange; + } + + Range lastRange = previousRange; + if (!lastRange.getHigh().isUpperUnbounded()) { + builder.add(new Range(lastRange.getHigh().greaterAdjacent(), Marker.upperUnbounded(allocator, type))); + } + + return builder.build(); + } + + private SortedRangeSet checkCompatibility(ValueSet other) + { + if (!getType().equals(other.getType())) { + throw new IllegalStateException(String.format("Mismatched types: %s vs %s", + getType(), other.getType())); + } + if (!(other instanceof SortedRangeSet)) { + throw new IllegalStateException(String.format("ValueSet is not a SortedRangeSet: %s", other.getClass())); + } + return (SortedRangeSet) other; + } + + private void checkTypeCompatibility(Marker marker) + { + if (!getType().equals(marker.getType())) { + throw new IllegalStateException(String.format("Marker of %s does not match SortedRangeSet of %s", + marker.getType(), getType())); + } + } + + @Override + public int hashCode() + { + return Objects.hash(lowIndexedRanges, nullAllowed); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + + final SortedRangeSet other = (SortedRangeSet) obj; + if (this.nullAllowed != other.isNullAllowed()) { + return false; + } + + return Objects.equals(this.lowIndexedRanges, other.lowIndexedRanges); + } + + @Override + public String toString() + { + return com.google.common.base.MoreObjects.toStringHelper(this) + .add("type", type) + .add("nullAllowed", nullAllowed) + .add("lowIndexedRanges", lowIndexedRanges) + .toString(); + } + + public static Builder newBuilder(ArrowType type, boolean nullAllowed) + { + return new Builder(type, nullAllowed); + } + + public static class Builder + { + private final ArrowType type; + private final boolean nullAllowed; + private final List ranges = new ArrayList<>(); + + Builder(ArrowType type, boolean nullAllowed) + { + requireNonNull(type, "type is null"); + this.type = type; + this.nullAllowed = nullAllowed; + } + + public Builder add(Range range) + { + if (!type.equals(range.getType())) { + throw new IllegalArgumentException(String.format("Range type %s does not match builder type %s", + range.getType(), type)); + } + + ranges.add(range); + return this; + } + + public Builder addAll(Iterable arg) + { + for (Range range : arg) { + add(range); + } + return this; + } + + public SortedRangeSet build() + { + Collections.sort(ranges, Comparator.comparing(Range::getLow)); + + NavigableMap result = new TreeMap<>(); + + Range current = null; + for (Range next : ranges) { + if (current == null) { + current = next; + continue; + } + + if (current.overlaps(next) || current.getHigh().isAdjacent(next.getLow())) { + current = current.span(next); + } + else { + result.put(current.getLow(), current); + current = next; + } + } + + if (current != null) { + result.put(current.getLow(), current); + } + + return new SortedRangeSet(type, result, nullAllowed); + } + } + + @Override + public void close() + throws Exception + { + for (Map.Entry next : lowIndexedRanges.entrySet()) { + next.getKey().close(); + next.getValue().close(); + } + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/ValueSet.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/ValueSet.java new file mode 100644 index 0000000000..60ee9a72e7 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/predicate/ValueSet.java @@ -0,0 +1,99 @@ +package com.amazonaws.athena.connector.lambda.domain.predicate; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import org.apache.arrow.vector.types.pojo.ArrowType; + +import java.beans.Transient; +import java.util.Collection; + +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.PROPERTY, + property = "@type") +@JsonSubTypes({ + @JsonSubTypes.Type(value = EquatableValueSet.class, name = "equatable"), + @JsonSubTypes.Type(value = SortedRangeSet.class, name = "sortable"), + @JsonSubTypes.Type(value = AllOrNoneValueSet.class, name = "allOrNone"), +}) +public interface ValueSet + extends AutoCloseable +{ + ArrowType getType(); + + @Transient + boolean isNone(); + + @Transient + boolean isAll(); + + @Transient + boolean isSingleValue(); + + @Transient + Object getSingleValue(); + + boolean isNullAllowed(); + + boolean containsValue(Marker value); + + /** + * @return range predicates for orderable Types + */ + @Transient + default Ranges getRanges() + { + throw new UnsupportedOperationException(); + } + + ValueSet intersect(BlockAllocator allocator, ValueSet other); + + ValueSet union(BlockAllocator allocator, ValueSet other); + + default ValueSet union(BlockAllocator allocator, Collection valueSets) + { + ValueSet current = this; + for (ValueSet valueSet : valueSets) { + current = current.union(allocator, valueSet); + } + return current; + } + + ValueSet complement(BlockAllocator allocator); + + default boolean overlaps(BlockAllocator allocator, ValueSet other) + { + return !this.intersect(allocator, other).isNone(); + } + + default ValueSet subtract(BlockAllocator allocator, ValueSet other) + { + return this.intersect(allocator, other.complement(allocator)); + } + + default boolean contains(BlockAllocator allocator, ValueSet other) + { + return this.union(allocator, other).equals(this); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/spill/S3SpillLocation.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/spill/S3SpillLocation.java new file mode 100644 index 0000000000..6f5f449820 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/spill/S3SpillLocation.java @@ -0,0 +1,176 @@ +package com.amazonaws.athena.connector.lambda.domain.spill; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.Objects; + +/** + * Defines a SpillLocation that is backed by S3. + */ +public class S3SpillLocation + implements SpillLocation +{ + private static final String SEPARATOR = "/"; + //The S3 bucket where we may have spilled data. + private final String bucket; + //The S3 key where we may have spilled data + private final String key; + //If true the Key is actually a key prefix for a location that may have multiple blocks. + private final boolean directory; + + /** + * Constructs an S3 SpillLocation. + * + * @param bucket The S3 bucket that is the root of the spill location. + * @param key The S3 key that represents the spill location + * @param directory Boolean that if True indicates the key is a pre-fix (aka directory) where multiple Blocks may + * be spilled. + */ + @JsonCreator + public S3SpillLocation(@JsonProperty("bucket") String bucket, + @JsonProperty("key") String key, + @JsonProperty("directory") boolean directory) + { + this.bucket = bucket; + this.key = key; + this.directory = directory; + } + + /** + * The S3 bucket that we may have spilled data to. + * + * @return String containing the S3 bucket name. + */ + @JsonProperty + public String getBucket() + { + return bucket; + } + + /** + * The S3 key that we may have spilled data to. + * + * @return String containing the S3 key. + */ + @JsonProperty + public String getKey() + { + return key; + } + + /** + * Indicates if the Key is actually a key prefix for a location that may have multiple blocks. + * + * @return True if the key is actually a prefix for a location that may have multiple blocks, False if the location + * points to a specific S3 object. + */ + @JsonProperty + public boolean isDirectory() + { + return directory; + } + + @Override + public String toString() + { + return "S3SpillLocation{" + + "bucket='" + bucket + '\'' + + ", key='" + key + '\'' + + ", directory=" + directory + + '}'; + } + + public static Builder newBuilder() + { + return new Builder(); + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + S3SpillLocation that = (S3SpillLocation) o; + return isDirectory() == that.isDirectory() && + Objects.equals(getBucket(), that.getBucket()) && + Objects.equals(getKey(), that.getKey()); + } + + @Override + public int hashCode() + { + return Objects.hash(getBucket(), getKey(), isDirectory()); + } + + public static class Builder + { + private String bucket; + private String prefix; + private String queryId; + private String splitId; + private boolean isDirectory = true; + + private Builder() {} + + public Builder withBucket(String bucket) + { + this.bucket = bucket; + return this; + } + + public Builder withPrefix(String prefix) + { + this.prefix = prefix; + return this; + } + + public Builder withIsDirectory(boolean isDirectory) + { + this.isDirectory = isDirectory; + return this; + } + + public Builder withQueryId(String queryId) + { + this.queryId = queryId; + return this; + } + + public Builder withSplitId(String splitId) + { + this.splitId = splitId; + return this; + } + + public S3SpillLocation build() + { + String key = prefix + SEPARATOR + queryId + SEPARATOR + splitId; + return new S3SpillLocation(bucket, key, true); + } + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/spill/SpillLocation.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/spill/SpillLocation.java new file mode 100644 index 0000000000..d4116c69f1 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/domain/spill/SpillLocation.java @@ -0,0 +1,37 @@ +package com.amazonaws.athena.connector.lambda.domain.spill; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +/** + * Used to tag different types of spill locations. + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY) +@JsonSubTypes({ + @JsonSubTypes.Type(value = S3SpillLocation.class, name = "S3SpillLocation") +}) +public interface SpillLocation +{ +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/examples/ContinuationToken.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/examples/ContinuationToken.java new file mode 100644 index 0000000000..fe0711c45c --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/examples/ContinuationToken.java @@ -0,0 +1,104 @@ +package com.amazonaws.athena.connector.lambda.examples; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +/** + * All items in the "com.amazonaws.athena.connector.lambda.examples" that this class belongs to are part of an + * 'Example' connector. We do not recommend using any of the classes in this package directly. Instead you can/should + * copy and modify as needed. + *

+ * This class is used to define the continuation token used by doGetSplits as well as logic for serializing and deserializing + * said token. + */ +public class ContinuationToken +{ + private static final String CONTINUATION_TOKEN_DIVIDER = ":"; + private final int partition; + private final int part; + + /** + * Basic constructor. + * + * @param partition The last partition we processed. + * @param part The next part to process. + */ + public ContinuationToken(int partition, int part) + { + this.partition = partition; + this.part = part; + } + + /** + * The partition in this token. + * + * @return int containing the last processed partition. + */ + public int getPartition() + { + return partition; + } + + /** + * The part in this token. + * + * @return int containing the next part to process. + */ + public int getPart() + { + return part; + } + + /** + * Decodes the provided String representation of a ContinuationToken into a ContinuationToken + * + * @param token An encoded ContinuationToken. + * @return The ContinuationToken represented by the token string. + */ + public static ContinuationToken decode(String token) + { + if (token != null) { + //if we have a continuation token, lets decode it. The format of this token is owned by this class + String[] tokenParts = token.split(CONTINUATION_TOKEN_DIVIDER); + + if (tokenParts.length != 2) { + throw new RuntimeException("Unable to decode continuation token " + token); + } + + int partition = Integer.valueOf(tokenParts[0]); + return new ContinuationToken(partition, Integer.valueOf(tokenParts[1])); + } + + //No continuation token present + return new ContinuationToken(0, 0); + } + + /** + * Encodes the provided partition and part into a string representation of ContinuationToken + * + * @param partition The last partition we processed. + * @param part The next part to process. + * @return The String representation of a ContinuationToken; + */ + public static String encode(int partition, int part) + { + return partition + CONTINUATION_TOKEN_DIVIDER + part; + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/examples/ExampleCompositeHandler.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/examples/ExampleCompositeHandler.java new file mode 100644 index 0000000000..2544fc95da --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/examples/ExampleCompositeHandler.java @@ -0,0 +1,36 @@ +package com.amazonaws.athena.connector.lambda.examples; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.handlers.CompositeHandler; + +/** + * Boilerplate composite handler that allows us to use a single Lambda function for both + * Metadata and Data. In this case we just compose ExampleMetadataHandler and ExampleRecordHandler. + */ +public class ExampleCompositeHandler + extends CompositeHandler +{ + public ExampleCompositeHandler() + { + super(new ExampleMetadataHandler(), new ExampleRecordHandler()); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/examples/ExampleMetadataHandler.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/examples/ExampleMetadataHandler.java new file mode 100644 index 0000000000..7b4f5c6589 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/examples/ExampleMetadataHandler.java @@ -0,0 +1,449 @@ +package com.amazonaws.athena.connector.lambda.examples; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockWriter; +import com.amazonaws.athena.connector.lambda.data.FieldBuilder; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.exceptions.FederationThrottleException; +import com.amazonaws.athena.connector.lambda.handlers.MetadataHandler; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesResponse; +import com.amazonaws.athena.connector.lambda.request.FederationRequest; +import com.amazonaws.athena.connector.lambda.request.PingRequest; +import com.amazonaws.athena.connector.lambda.security.EncryptionKey; +import com.amazonaws.athena.connector.lambda.security.EncryptionKeyFactory; +import com.amazonaws.athena.connector.lambda.security.FederatedIdentity; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import org.apache.arrow.util.VisibleForTesting; +import org.apache.arrow.vector.complex.reader.FieldReader; +import org.apache.arrow.vector.types.DateUnit; +import org.apache.arrow.vector.types.FloatingPointPrecision; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.arrow.vector.types.pojo.Schema; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * All items in the "com.amazonaws.athena.connector.lambda.examples" that this class belongs to are part of an + * 'Example' connector. We do not recommend using any of the classes in this package directly. Instead you can/should + * copy and modify as needed. + *

+ * This class defined an example MetadataHandler that supports a single schema and single table which showcases most + * of the features offered by the Amazon Athena Query Federation SDK. Some notable characteristics include: + * 1. Highly partitioned table. + * 2. Paginated split generation. + * 3. S3 Spill support. + * 4. Spill encryption using either KMS KeyFactory or LocalKeyFactory. + * 5. A wide range of field types including complex Struct and List types. + *

+ * + * @note All schema names, table names, and column names must be lower case at this time. Any entities that are uppercase or + * mixed case will not be accessible in queries and will be lower cased by Athena's engine to ensure consistency across + * sources. As such you may need to handle this when integrating with a source that supports mixed case. As an example, + * you can look at the CloudwatchTableResolver in the athena-cloudwatch module for one potential approach to this challenge. + *

+ * @see MetadataHandler + */ +public class ExampleMetadataHandler + extends MetadataHandler +{ + private static final Logger logger = LoggerFactory.getLogger(ExampleMetadataHandler.class); + //Used to aid in diagnostic logging + private static final String SOURCE_TYPE = "custom"; + //The name of the Lambda Environment vaiable that toggles generating simulated Throttling events to trigger Athena's + //Congestion control logic. + private static final String SIMULATE_THROTTLES = "SIMULATE_THROTTLES"; + //The number of splits to generated for each Partition. Keep in mind this connector generates random data, a real + //source is unlikely to have such a setting. + protected static final int NUM_PARTS_PER_SPLIT = 10; + //This is used to illustrate how to use continuation tokens to handle partitions that generate a large number + //of splits. This helps avoid hitting the Lambda response size limit. + protected static final int MAX_SPLITS_PER_REQUEST = 300; + //Field name for storing partition location information. + protected static final String PARTITION_LOCATION = "location"; + //Field name for storing an example property on our partitions and splits. + protected static final String SERDE = "serde"; + + //Stores how frequently to generate a simulated throttling event. + private final int simulateThrottle; + //Controls if spill encryption should be enabled or disabled. + private boolean encryptionEnabled = true; + //Counter that is used in conjunction with simulateThrottle to generated simulated throttling events. + private int count = 0; + + /** + * Default constructor used by Lambda. + */ + public ExampleMetadataHandler() + { + super(SOURCE_TYPE); + this.simulateThrottle = (System.getenv(SIMULATE_THROTTLES) == null) ? 0 : Integer.parseInt(System.getenv(SIMULATE_THROTTLES)); + } + + /** + * Full DI constructor used mostly for testing + * + * @param keyFactory The EncryptionKeyFactory to use for spill encryption. + * @param awsSecretsManager The AWSSecretsManager client that can be used when attempting to resolve secrets. + * @param athena The Athena client that can be used to fetch query termination status to fast-fail this handler. + * @param spillBucket The S3 Bucket to use when spilling results. + * @param spillPrefix The S3 prefix to use when spilling results. + */ + @VisibleForTesting + protected ExampleMetadataHandler(EncryptionKeyFactory keyFactory, + AWSSecretsManager awsSecretsManager, + AmazonAthena athena, + String spillBucket, + String spillPrefix) + { + super(keyFactory, awsSecretsManager, athena, SOURCE_TYPE, spillBucket, spillPrefix); + //Read the Lambda environment variable for controlling simulated throttles. + this.simulateThrottle = (System.getenv(SIMULATE_THROTTLES) == null) ? 0 : Integer.parseInt(System.getenv(SIMULATE_THROTTLES)); + } + + /** + * Used to toggle encryption during unit tests. + * + * @param enableEncryption + */ + @VisibleForTesting + protected void setEncryption(boolean enableEncryption) + { + this.encryptionEnabled = enableEncryption; + } + + /** + * Demonstrates how you can capture the identity of the caller that ran the Athena query which triggered the Lambda invocation. + * + * @param request + */ + private void logCaller(FederationRequest request) + { + FederatedIdentity identity = request.getIdentity(); + logger.info("logCaller: account[" + identity.getAccount() + "] id[" + identity.getId() + "] principal[" + identity.getPrincipal() + "]"); + } + + /** + * Returns a static, single schema. A connector for a real data source would likely query that source's metadata + * to create a real list of schemas. + * + * @param allocator Tool for creating and managing Apache Arrow Blocks. + * @param request Provides details on who made the request and which Athena catalog they are querying. + * @return The ListSchemasResponse which mostly contains the list of schemas (aka databases). + */ + @Override + public ListSchemasResponse doListSchemaNames(BlockAllocator allocator, ListSchemasRequest request) + { + logCaller(request); + List schemas = new ArrayList<>(); + schemas.add(ExampleTable.schemaName); + return new ListSchemasResponse(request.getCatalogName(), schemas); + } + + /** + * Returns a static list of TableNames. A connector for a real data source would likely query that source's metadata + * to create a real list of TableNames for the requested schema name. + * + * @param allocator Tool for creating and managing Apache Arrow Blocks. + * @param request Provides details on who made the request and which Athena catalog and database they are querying. + * @return A ListTablesResponse containing the list of available TableNames. + */ + @Override + public ListTablesResponse doListTables(BlockAllocator allocator, ListTablesRequest request) + { + logCaller(request); + List tables = new ArrayList<>(); + tables.add(new TableName(ExampleTable.schemaName, ExampleTable.tableName)); + + //The below filter for null schema is not typical, we do this to generate a specific semantic error + //that is exercised in our unit test suite. + return new ListTablesResponse(request.getCatalogName(), + tables.stream() + .filter(table -> request.getSchemaName() == null || request.getSchemaName().equals(table.getSchemaName())) + .collect(Collectors.toList())); + } + + /** + * Retrieves a static Table schema for the example table. A connector for a real data source would likely query that + * source's metadata to create a table definition. + * + * @param allocator Tool for creating and managing Apache Arrow Blocks. + * @param request Provides details on who made the request and which Athena catalog, database, and table they are querying. + * @return A GetTableResponse containing the definition of the table (e.g. table schema and partition columns) + */ + @Override + public GetTableResponse doGetTable(BlockAllocator allocator, GetTableRequest request) + { + logCaller(request); + if (!request.getTableName().getSchemaName().equals(ExampleTable.schemaName) || + !request.getTableName().getTableName().equals(ExampleTable.tableName)) { + throw new IllegalArgumentException("Unknown table " + request.getTableName()); + } + + Set partitionCols = new HashSet<>(); + partitionCols.add("month"); + partitionCols.add("year"); + partitionCols.add("day"); + return new GetTableResponse(request.getCatalogName(), request.getTableName(), ExampleTable.schema, partitionCols); + } + + /** + * Here we inject the two additional columns we define for partition metadata. These columns are ignored by + * Athena but passed along to our code when Athena calls GetSplits(...). If you do not require any additional + * metadata on your partitions you may choose not to implement this function. + * + * @param partitionSchemaBuilder The SchemaBuilder you can use to add additional columns and metadata to the + * partitions response. + * @param request The GetTableLayoutResquest that triggered this call. + */ + @Override + public void enhancePartitionSchema(SchemaBuilder partitionSchemaBuilder, GetTableLayoutRequest request) + { + /** + * Add any additional fields we might need to our partition response schema. + * These additional fields are ignored by Athena but will be passed to GetSplits(...) + * when Athena calls our lambda function to plan the distributed read of our partitions. + */ + partitionSchemaBuilder.addField(PARTITION_LOCATION, new ArrowType.Utf8()) + .addField(SERDE, new ArrowType.Utf8()); + } + + /** + * Our example table is partitions on year, month, day so we loop over a range of years, months, and days to generate + * our example partitions. A connector for a real data source would likely query that source's metadata + * to create a real list of partitions. + * @param writer Used to write rows (partitions) into the Apache Arrow response. The writes are automatically constrained. + * @param request Provides details of the catalog, database, and table being queried as well as any filter predicate. + * @param queryStatusChecker + */ + @Override + public void getPartitions(BlockWriter writer, GetTableLayoutRequest request, QueryStatusChecker queryStatusChecker) + { + logCaller(request); + + /** + * Now use the constraint that was in the request to do some partition pruning. Here we are just + * generating some fake values for the partitions but in a real implementation you'd use your metastore + * or knowledge of the actual table's physical layout to do this. + */ + for (int year = 1990; year < 2020; year++) { + for (int month = 0; month < 12; month++) { + for (int day = 0; day < 30; day++) { + final int dayVal = day; + final int monthVal = month; + final int yearVal = year; + writer.writeRows((Block block, int rowNum) -> { + //these are our partition columns and were defined by the call to doGetTable(...) + boolean matched = true; + matched &= block.setValue("day", rowNum, dayVal); + matched &= block.setValue("month", rowNum, monthVal); + matched &= block.setValue("year", rowNum, yearVal); + + //these are additional field we added by overriding enhancePartitionSchema(...) + matched &= block.setValue(PARTITION_LOCATION, rowNum, "s3://" + request.getPartitionCols()); + matched &= block.setValue(SERDE, rowNum, "TextInputFormat"); + + //if all fields passed then we wrote 1 row + return matched ? 1 : 0; + }); + } + } + } + } + + /** + * For each partition we generate a pre-determined number of splits based on the NUM_PARTS_PER_SPLIT setting. This + * method also demonstrates how to handle calls for batches of partitions and also leverage this API's ability + * to paginated. A connector for a real data source would likely query that source's metadata to determine if/how + * to split up the read operations for a particular partition. + * + * @param allocator Tool for creating and managing Apache Arrow Blocks. + * @param request Provides details of the catalog, database, table, andpartition(s) being queried as well as + * any filter predicate. + * @return A GetSplitsResponse which contains a list of splits as an optional continuation token if we were not + * able to generate all splits for the partitions in this batch. + */ + @Override + public GetSplitsResponse doGetSplits(BlockAllocator allocator, GetSplitsRequest request) + { + logCaller(request); + logger.info("doGetSplits: spill location " + makeSpillLocation(request)); + + /** + * It is important to try and throw any throttling events before writing data since Athena may not be able to + * continue the query, due to consistency errors, if you throttle after writing data. + */ + if (simulateThrottle > 0 && count++ % simulateThrottle == 0) { + logger.info("readWithConstraint: throwing throttle Exception!"); + throw new FederationThrottleException("Please slow down for this simulated throttling event"); + } + + ContinuationToken requestToken = ContinuationToken.decode(request.getContinuationToken()); + int partitionContd = requestToken.getPartition(); + int partContd = requestToken.getPart(); + + Set splits = new HashSet<>(); + Block partitions = request.getPartitions(); + for (int curPartition = partitionContd; curPartition < partitions.getRowCount(); curPartition++) { + //We use the makeEncryptionKey() method from our parent class to make an EncryptionKey + EncryptionKey encryptionKey = makeEncryptionKey(); + + //We prepare to read our custom metadata fields from the partition so that we can pass this info to the split(s) + FieldReader locationReader = partitions.getFieldReader(SplitProperties.LOCATION.getId()); + locationReader.setPosition(curPartition); + FieldReader storageClassReader = partitions.getFieldReader(SplitProperties.SERDE.getId()); + storageClassReader.setPosition(curPartition); + + //Do something to decide if this partition needs to be subdivided into multiple, possibly concurrent, + //table scan operations (aka splits) + for (int curPart = partContd; curPart < NUM_PARTS_PER_SPLIT; curPart++) { + if (splits.size() >= MAX_SPLITS_PER_REQUEST) { + //We exceeded the number of split we want to return in a single request, return and provide + //a continuation token. + return new GetSplitsResponse(request.getCatalogName(), + splits, + ContinuationToken.encode(curPartition, curPart)); + } + + //We use makeSpillLocation(...) from our parent class to get a unique SpillLocation for each split + Split.Builder splitBuilder = Split.newBuilder(makeSpillLocation(request), encryptionEnabled ? encryptionKey : null) + .add(SplitProperties.LOCATION.getId(), String.valueOf(locationReader.readText())) + .add(SplitProperties.SERDE.getId(), String.valueOf(storageClassReader.readText())) + .add(SplitProperties.SPLIT_PART.getId(), String.valueOf(curPart)); + + //Add the partition column values to the split's properties. + //We are doing this because our example record reader depends on it, your specific needs + //will likely vary. Our example only supports a limited number of partition column types. + for (String next : request.getPartitionCols()) { + FieldReader reader = partitions.getFieldReader(next); + reader.setPosition(curPartition); + + switch (reader.getMinorType()) { + case UINT2: + splitBuilder.add(next, Integer.valueOf(reader.readCharacter()).toString()); + break; + case UINT4: + case INT: + splitBuilder.add(next, String.valueOf(reader.readInteger())); + break; + case UINT8: + case BIGINT: + splitBuilder.add(next, String.valueOf(reader.readLong())); + break; + default: + throw new RuntimeException("Unsupported partition column type. " + reader.getMinorType()); + } + } + + splits.add(splitBuilder.build()); + } + + //part continuation only applies within a partition so we complete that partial partition and move on + //to the next one. + partContd = 0; + } + + return new GetSplitsResponse(request.getCatalogName(), splits, null); + } + + /** + * We use the ping signal to simply log the fact that a ping request came in. + * + * @param request The PingRequest. + */ + public void onPing(PingRequest request) + { + logCaller(request); + } + + /** + * We use this as our static metastore for the example implementation + */ + protected static class ExampleTable + { + public static final String schemaName = "custom_source"; + public static final String tableName = "fake_table"; + public static final Schema schema; + + static { + schema = new SchemaBuilder().newBuilder() + .addField("col1", new ArrowType.Date(DateUnit.DAY)) + .addField("day", new ArrowType.Int(32, true)) + .addField("month", new ArrowType.Int(32, true)) + .addField("year", new ArrowType.Int(32, true)) + .addField("col3", new ArrowType.Bool()) + .addField("col4", new ArrowType.FloatingPoint(FloatingPointPrecision.DOUBLE)) + .addField("col5", new ArrowType.Utf8()) + .addField("datemilli", Types.MinorType.DATEMILLI.getType()) + .addField("int", Types.MinorType.INT.getType()) + .addField("tinyint", Types.MinorType.TINYINT.getType()) + .addField("smallint", Types.MinorType.SMALLINT.getType()) + .addField("bigint", Types.MinorType.BIGINT.getType()) + .addField("float4", Types.MinorType.FLOAT4.getType()) + .addField("float8", Types.MinorType.FLOAT8.getType()) + .addField("bit", Types.MinorType.BIT.getType()) + .addField("varchar", Types.MinorType.VARCHAR.getType()) + .addField("varbinary", Types.MinorType.VARBINARY.getType()) + .addField("decimal", new ArrowType.Decimal(10, 2)) + .addField("decimalLong", new ArrowType.Decimal(36, 2)) + //Example of a List of Structs + .addField( + FieldBuilder.newBuilder("list", new ArrowType.List()) + .addField( + FieldBuilder.newBuilder("innerStruct", Types.MinorType.STRUCT.getType()) + .addStringField("varchar") + .addBigIntField("bigint") + .build()) + .build()) + //Example of a List Of Lists + .addField( + FieldBuilder.newBuilder("outerlist", new ArrowType.List()) + .addListField("innerList", Types.MinorType.VARCHAR.getType()) + .build()) + .addMetadata("partitionCols", "day,month,year") + .addMetadata("randomProp1", "randomPropVal1") + .addMetadata("randomProp2", "randomPropVal2").build(); + } + + private ExampleTable() {} + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/examples/ExampleRecordHandler.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/examples/ExampleRecordHandler.java new file mode 100644 index 0000000000..4e1b058725 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/examples/ExampleRecordHandler.java @@ -0,0 +1,309 @@ +package com.amazonaws.athena.connector.lambda.examples; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockSpiller; +import com.amazonaws.athena.connector.lambda.data.FieldResolver; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.exceptions.FederationThrottleException; +import com.amazonaws.athena.connector.lambda.handlers.RecordHandler; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.athena.connector.lambda.request.FederationRequest; +import com.amazonaws.athena.connector.lambda.request.PingRequest; +import com.amazonaws.athena.connector.lambda.security.FederatedIdentity; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.athena.AmazonAthenaClientBuilder; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.amazonaws.services.secretsmanager.AWSSecretsManagerClientBuilder; +import org.apache.arrow.util.VisibleForTesting; +import org.apache.arrow.vector.FieldVector; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Field; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * All items in the "com.amazonaws.athena.connector.lambda.examples" that this class belongs to are part of an + * 'Example' connector. We do not recommend using any of the classes in this package directly. Instead you can/should + * copy and modify as needed. + *

+ * More specifically, this class is responsible for providing Athena with actual rows level data from our simulated + * source. Athena will call readWithConstraint(...) on this class for each 'Split' we generated in ExampleMetadataHandler. + *

+ * + * @see com.amazonaws.athena.connector.lambda.handlers.RecordHandler + */ +public class ExampleRecordHandler + extends RecordHandler +{ + private static final Logger logger = LoggerFactory.getLogger(ExampleRecordHandler.class); + //used in diagnostic logging + private static final String SOURCE_TYPE = "custom"; + //The name of the environment variable to read for the number of rows to generate per Split instead of + //the default below. + private static final String NUM_ROWS_PER_SPLIT = "NUM_ROWS_PER_SPLIT"; + //The name of the Lambda Environment vaiable that toggles generating simulated Throttling events to trigger Athena's + //Congestion control logic. + private static final String SIMULATE_THROTTLES = "SIMULATE_THROTTLES"; + //The number of rows to generate per Split + private int numRowsPerSplit = 400_000; + //Stores how frequently to generate a simulated throttling event. + private final int simulateThrottle; + //Counter that is used in conjunction with simulateThrottle to generated simulated throttling events. + private int count = 0; + + /** + * Default constructor used by Lambda. + */ + public ExampleRecordHandler() + { + this(AmazonS3ClientBuilder.defaultClient(), AWSSecretsManagerClientBuilder.defaultClient(), AmazonAthenaClientBuilder.defaultClient()); + if (System.getenv(NUM_ROWS_PER_SPLIT) != null) { + numRowsPerSplit = Integer.parseInt(System.getenv(NUM_ROWS_PER_SPLIT)); + } + } + + /** + * Full DI constructor used mostly for testing + * + * @param amazonS3 The AmazonS3 client to use for spills. + * @param secretsManager The AWSSecretsManager client that can be used when attempting to resolve secrets. + * @param athena The Athena client that can be used to fetch query termination status to fast-fail this handler. + */ + @VisibleForTesting + protected ExampleRecordHandler(AmazonS3 amazonS3, AWSSecretsManager secretsManager, AmazonAthena athena) + { + super(amazonS3, secretsManager, athena, SOURCE_TYPE); + this.simulateThrottle = (System.getenv(SIMULATE_THROTTLES) == null) ? 0 : Integer.parseInt(System.getenv(SIMULATE_THROTTLES)); + } + + /** + * Used to set the number of rows per split. This method is mostly used for testing where setting the environment + * variable to override the default is not practical. + * + * @param numRows The number of rows to generate per split. + */ + @VisibleForTesting + protected void setNumRows(int numRows) + { + this.numRowsPerSplit = numRows; + } + + /** + * Demonstrates how you can capture the identity of the caller that ran the Athena query which triggered the Lambda invocation. + * + * @param request + */ + private void logCaller(FederationRequest request) + { + FederatedIdentity identity = request.getIdentity(); + logger.info("logCaller: account[" + identity.getAccount() + "] id[" + identity.getId() + "] principal[" + identity.getPrincipal() + "]"); + } + + /** + * We use the ping signal to simply log the fact that a ping request came in. + * + * @param request The PingRequest. + */ + protected void onPing(PingRequest request) + { + logCaller(request); + } + + /** + * Here we generate our simulated row data. A real connector would instead connect to the actual source and read + * the data corresponding to the requested split. + * @param spiller A BlockSpiller that should be used to write the row data associated with this Split. + * The BlockSpiller automatically handles applying constraints, chunking the response, encrypting, and spilling to S3. + * @param request The ReadRecordsRequest containing the split and other details about what to read. + * @param queryStatusChecker A QueryStatusChecker that you can use to stop doing work for a query that has already terminated + */ + @Override + protected void readWithConstraint(BlockSpiller spiller, ReadRecordsRequest request, QueryStatusChecker queryStatusChecker) + { + /** + * It is important to try and throw any throttling events before writing data since Athena may not be able to + * continue the query, due to consistency errors, if you throttle after writing data. + */ + if (simulateThrottle > 0 && count++ % simulateThrottle == 0) { + logger.info("readWithConstraint: throwing throttle Exception!"); + throw new FederationThrottleException("Please slow down for this simulated throttling event"); + } + + logCaller(request); + for (int i = 0; i < numRowsPerSplit; i++) { + if (!queryStatusChecker.isQueryRunning()) { + return; + } + final int seed = i; + spiller.writeRows((Block block, int rowNum) -> { + //This is just filling the row with random data and then partition values that match the split + //in a real implementation you would read your real data. + boolean rowMatched = makeRandomRow(block, rowNum, seed); + addPartitionColumns(request.getSplit(), block, rowNum); + return rowMatched ? 1 : 0; + }); + } + } + + /** + * Helper function that we use to ensure the partition columns values are not randomly generated and instead + * correspond to the partition that the Split belongs to. This is important because if they do not match + * then the rows will likely get filtered out of the result. This method is only applicable to our random + * row data as a real connector would not have to worry about a missmatch of these values because they would + * of course match their storage. + * + * @param split The Split that we are generating partition column values for. + * @param block The Block we need to write the partition column values into. + * @param blockRow The row twe need to write the partition column values into. + */ + private void addPartitionColumns(Split split, Block block, int blockRow) + { + for (String nextPartition : ExampleMetadataHandler.ExampleTable.schema.getCustomMetadata().get("partitionCols").split(",")) { + FieldVector vector = block.getFieldVector(nextPartition); + if (vector != null) { + switch (vector.getMinorType()) { + case INT: + case UINT2: + case BIGINT: + block.setValue(nextPartition, blockRow, Integer.valueOf(split.getProperty(nextPartition))); + break; + default: + throw new RuntimeException(vector.getMinorType() + " is not supported"); + } + } + } + } + + /** + * This should be replaced with something that actually reads useful data. + */ + private boolean makeRandomRow(Block block, int blockRow, int seed) + { + Set partitionCols = new HashSet<>(); + String partitionColsMetadata = block.getSchema().getCustomMetadata().get("partitionCols"); + if (partitionColsMetadata != null) { + partitionCols.addAll(Arrays.asList(partitionColsMetadata.split(","))); + } + + boolean matches = true; + for (Field next : block.getSchema().getFields()) { + String fieldName = next.getName(); + if (!partitionCols.contains(fieldName)) { + if (!matches) { + return false; + } + boolean negative = seed % 2 == 1; + Types.MinorType fieldType = Types.getMinorTypeForArrowType(next.getType()); + switch (fieldType) { + case INT: + int iVal = seed * (negative ? -1 : 1); + matches &= block.setValue(fieldName, blockRow, iVal); + break; + case DATEMILLI: + matches &= block.setValue(fieldName, blockRow, 100_000L); + break; + case DATEDAY: + matches &= block.setValue(fieldName, blockRow, 100_000); + break; + case TINYINT: + case SMALLINT: + int stVal = (seed % 4) * (negative ? -1 : 1); + matches &= block.setValue(fieldName, blockRow, stVal); + break; + case UINT1: + case UINT2: + case UINT4: + case UINT8: + int uiVal = seed % 4; + matches &= block.setValue(fieldName, blockRow, uiVal); + break; + case FLOAT4: + float fVal = seed * 1.1f * (negative ? -1 : 1); + matches &= block.setValue(fieldName, blockRow, fVal); + break; + case FLOAT8: + case DECIMAL: + double d8Val = seed * 1.1D * (negative ? -1 : 1); + matches &= block.setValue(fieldName, blockRow, d8Val); + break; + case BIT: + boolean bVal = seed % 2 == 0; + matches &= block.setValue(fieldName, blockRow, bVal); + break; + case BIGINT: + long lVal = seed * 1L * (negative ? -1 : 1); + matches &= block.setValue(fieldName, blockRow, lVal); + break; + case VARCHAR: + String vVal = "VarChar" + seed; + matches &= block.setValue(fieldName, blockRow, vVal); + break; + case VARBINARY: + byte[] binaryVal = ("VarChar" + seed).getBytes(); + matches &= block.setValue(fieldName, blockRow, binaryVal); + break; + case LIST: + //This is setup for the specific kinds of lists we have in our example schema, + //it is not universal. List and List is what + //this block supports. + Field child = block.getFieldVector(fieldName).getField().getChildren().get(0); + List value = new ArrayList<>(); + Types.MinorType childType = Types.getMinorTypeForArrowType(child.getType()); + switch (childType) { + case LIST: + List list = new ArrayList<>(); + list.add(String.valueOf(1000)); + list.add(String.valueOf(1001)); + list.add(String.valueOf(1002)); + value.add(list); + break; + case STRUCT: + Map struct = new HashMap<>(); + struct.put("varchar", "chars"); + struct.put("bigint", 100L); + value.add(struct); + break; + default: + throw new RuntimeException(childType + " is not supported"); + } + matches &= block.setComplexValue(fieldName, blockRow, FieldResolver.DEFAULT, value); + break; + default: + throw new RuntimeException(fieldType + " is not supported"); + } + } + } + return matches; + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/examples/ExampleUserDefinedFunctionHandler.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/examples/ExampleUserDefinedFunctionHandler.java new file mode 100644 index 0000000000..a6b2184b4e --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/examples/ExampleUserDefinedFunctionHandler.java @@ -0,0 +1,118 @@ +package com.amazonaws.athena.connector.lambda.examples; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.udf.UserDefinedFunctionHandler; +import com.google.common.collect.ImmutableMap; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class ExampleUserDefinedFunctionHandler + extends UserDefinedFunctionHandler +{ + public Boolean example_udf(Boolean value) + { + return !value; + } + + public Byte example_udf(Byte value) + { + return (byte) (value + 1); + } + + public Short example_udf(Short value) + { + return (short) (value + 1); + } + + public Integer example_udf(Integer value) + { + return value + 1; + } + + public Long example_udf(Long value) + { + return value + 1; + } + + public Float example_udf(Float value) + { + return value + 1; + } + + public Double example_udf(Double value) + { + return value + 1; + } + + public BigDecimal example_udf(BigDecimal value) + { + BigDecimal one = new BigDecimal(1); + one.setScale(value.scale(), RoundingMode.HALF_UP); + return value.add(one); + } + + public String example_udf(String value) + { + return value + "_dada"; + } + + public LocalDateTime example_udf(LocalDateTime value) + { + return value.minusDays(1); + } + + public LocalDate example_udf(LocalDate value) + { + return value.minusDays(1); + } + + public List example_udf(List value) + { + System.out.println("Array input: " + value); + List result = value.stream().map(o -> ((Integer) o) + 1).collect(Collectors.toList()); + System.out.println("Array output: " + result); + return result; + } + + public Map example_udf(Map value) + { + Long longVal = (Long) value.get("x"); + Double doubleVal = (Double) value.get("y"); + + return ImmutableMap.of("x", longVal + 1, "y", doubleVal + 1.0); + } + + public byte[] example_udf(byte[] value) + { + byte[] output = new byte[value.length]; + for (int i = 0; i < value.length; ++i) { + output[i] = (byte) (value[i] + 1); + } + return output; + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/examples/SplitProperties.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/examples/SplitProperties.java new file mode 100644 index 0000000000..a9bd10bf3f --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/examples/SplitProperties.java @@ -0,0 +1,40 @@ +package com.amazonaws.athena.connector.lambda.examples; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +public enum SplitProperties +{ + LOCATION(ExampleMetadataHandler.PARTITION_LOCATION), + SERDE(ExampleMetadataHandler.SERDE), + SPLIT_PART("SPLIT_PART"); + + private final String id; + + SplitProperties(String id) + { + this.id = id; + } + + public String getId() + { + return id; + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/exceptions/FederationThrottleException.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/exceptions/FederationThrottleException.java new file mode 100644 index 0000000000..5a920096e4 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/exceptions/FederationThrottleException.java @@ -0,0 +1,54 @@ +package com.amazonaws.athena.connector.lambda.exceptions; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +/** + * Throw this exception if your source is unable to keep up with the rate or concurrency + * of requests. Athena constantly monitors for performance of federated sources and + * employs a congestion control mechanism to reduce pressure on sources that may be + * overwhelmed or unable to keep up. Throwing this exception gives Athena important + * back pressure information. Alternatively you can reduce the concurrency of the + * affected Lambda function in the Lambda console which will cause Lambda to generate + * Throttle exceptions for Athena. + * + * @note If you throw this exception after writing any data Athena may fail the operation + * to ensure consistency of your results. This is because Athena eagerly processes + * your results but is unsure if two identical calls to your source will produce + * the exact same result set (including ordering). + */ +public class FederationThrottleException + extends RuntimeException +{ + public FederationThrottleException() + { + super(); + } + + public FederationThrottleException(String message) + { + super(message); + } + + public FederationThrottleException(String message, Throwable cause) + { + super(message, cause); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/handlers/AthenaExceptionFilter.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/handlers/AthenaExceptionFilter.java new file mode 100644 index 0000000000..9a9d7e2c46 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/handlers/AthenaExceptionFilter.java @@ -0,0 +1,37 @@ +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connector.lambda.handlers; + +import com.amazonaws.athena.connector.lambda.ThrottlingInvoker; +import com.amazonaws.services.athena.model.TooManyRequestsException; + +public class AthenaExceptionFilter + implements ThrottlingInvoker.ExceptionFilter +{ + public static final ThrottlingInvoker.ExceptionFilter ATHENA_EXCEPTION_FILTER = new AthenaExceptionFilter(); + + private AthenaExceptionFilter() {} + + @Override + public boolean isMatch(Exception ex) + { + return ex instanceof TooManyRequestsException; + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/handlers/CompositeHandler.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/handlers/CompositeHandler.java new file mode 100644 index 0000000000..7638feb3ff --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/handlers/CompositeHandler.java @@ -0,0 +1,133 @@ +package com.amazonaws.athena.connector.lambda.handlers; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.amazonaws.athena.connector.lambda.metadata.MetadataRequest; +import com.amazonaws.athena.connector.lambda.records.RecordRequest; +import com.amazonaws.athena.connector.lambda.request.FederationRequest; +import com.amazonaws.athena.connector.lambda.request.FederationResponse; +import com.amazonaws.athena.connector.lambda.request.PingRequest; +import com.amazonaws.athena.connector.lambda.request.PingResponse; +import com.amazonaws.athena.connector.lambda.serde.ObjectMapperFactory; +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestStreamHandler; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * This class allows you to have a single Lambda function be responsible for both metadata and data operations by + * composing a MetadataHandler with a RecordHandler and muxing requests to the appropriate class. You might choose + * to use this CompositeHandler to run a single lambda function for the following reasons: + * 1. Can be simpler to deploy and manage a single vs multiple Lambda functions + * 2. You don't need to independently control the cost or performance of metadata vs. data operations. + * + * @see RequestStreamHandler + */ +public class CompositeHandler + implements RequestStreamHandler +{ + private static final Logger logger = LoggerFactory.getLogger(CompositeHandler.class); + //The MetadataHandler to delegate metadata operations to. + private final MetadataHandler metadataHandler; + //The RecordHandler to delegate data operations to. + private final RecordHandler recordHandler; + + /** + * Basic constructor that composes a MetadataHandler with a RecordHandler. + * + * @param metadataHandler The MetadataHandler to delegate metadata operations to. + * @param recordHandler The RecordHandler to delegate data operations to. + */ + public CompositeHandler(MetadataHandler metadataHandler, RecordHandler recordHandler) + { + this.metadataHandler = metadataHandler; + this.recordHandler = recordHandler; + } + + /** + * Required by Lambda's RequestStreamHandler interface. In our case we use this method to handle some + * basic resource lifecycle tasks for the request, namely the BlockAllocator and the request object itself. + */ + public final void handleRequest(InputStream inputStream, OutputStream outputStream, final Context context) + throws IOException + { + try (BlockAllocatorImpl allocator = new BlockAllocatorImpl()) { + ObjectMapper objectMapper = ObjectMapperFactory.create(allocator); + try (FederationRequest rawReq = objectMapper.readValue(inputStream, FederationRequest.class)) { + handleRequest(allocator, rawReq, outputStream, objectMapper); + } + } + catch (Exception ex) { + logger.warn("handleRequest: Completed with an exception.", ex); + throw (ex instanceof RuntimeException) ? (RuntimeException) ex : new RuntimeException(ex); + } + } + + /** + * Handles routing the request to the appropriate Handler, either MetadataHandler or RecordHandler. + * + * @param allocator The BlockAllocator to use for Apache Arrow Resources. + * @param rawReq The request object itself. + * @param outputStream The OutputStream to which all responses should be written. + * @param objectMapper The ObjectMapper that can be used for serializing responses. + * @throws Exception + * @note that PingRequests are routed to the MetadataHandler even though both MetadataHandler and RecordHandler + * implemented PingRequest handling. + */ + public final void handleRequest(BlockAllocator allocator, FederationRequest rawReq, OutputStream outputStream, ObjectMapper objectMapper) + throws Exception + { + if (rawReq instanceof PingRequest) { + try (PingResponse response = metadataHandler.doPing((PingRequest) rawReq)) { + assertNotNull(response); + objectMapper.writeValue(outputStream, response); + } + return; + } + + if (rawReq instanceof MetadataRequest) { + metadataHandler.doHandleRequest(allocator, objectMapper, (MetadataRequest) rawReq, outputStream); + } + else if (rawReq instanceof RecordRequest) { + recordHandler.doHandleRequest(allocator, objectMapper, (RecordRequest) rawReq, outputStream); + } + else { + throw new IllegalArgumentException("Unknown request class " + rawReq.getClass()); + } + } + + /** + * Helper used to assert that the response generated by the handler is not null. + */ + private void assertNotNull(FederationResponse response) + { + if (response == null) { + throw new RuntimeException("Response was null"); + } + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/handlers/FederationCapabilities.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/handlers/FederationCapabilities.java new file mode 100644 index 0000000000..43508b60c1 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/handlers/FederationCapabilities.java @@ -0,0 +1,34 @@ +package com.amazonaws.athena.connector.lambda.handlers; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +/** + * Used to convey the capabilities of this SDK instance when negotiating functionality with + * Athena. You can think of this like a version number that is specific to the feature set + * and protocol used by the SDK. Purely client side changes in the SDK would not be expected + * to change the capabilities. + */ +public class FederationCapabilities +{ + private FederationCapabilities() {} + + protected static final int CAPABILITIES = 23; +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/handlers/GlueMetadataHandler.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/handlers/GlueMetadataHandler.java new file mode 100644 index 0000000000..d4264013a5 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/handlers/GlueMetadataHandler.java @@ -0,0 +1,330 @@ +package com.amazonaws.athena.connector.lambda.handlers; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesResponse; +import com.amazonaws.athena.connector.lambda.metadata.MetadataRequest; +import com.amazonaws.athena.connector.lambda.metadata.glue.GlueFieldLexer; +import com.amazonaws.athena.connector.lambda.security.EncryptionKeyFactory; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.glue.AWSGlue; +import com.amazonaws.services.glue.model.Column; +import com.amazonaws.services.glue.model.Database; +import com.amazonaws.services.glue.model.GetDatabasesRequest; +import com.amazonaws.services.glue.model.GetDatabasesResult; +import com.amazonaws.services.glue.model.GetTableResult; +import com.amazonaws.services.glue.model.GetTablesRequest; +import com.amazonaws.services.glue.model.GetTablesResult; +import com.amazonaws.services.glue.model.Table; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import org.apache.arrow.util.VisibleForTesting; +import org.apache.arrow.vector.types.pojo.Field; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * This class allows you to leverage AWS Glue's DataCatalog to satisfy portions of the functionality required in a + * MetadataHandler. More precisely, this implementation uses AWS Glue's DataCatalog to implement: + * 1. doListSchemas(...) + * 2. doListTables(...) + * 3. doGetTable(...) + *

+ * When you extend this class you can optionally provide a DatabaseFilter and/or TableFilter to decide which Databases + * (aka schemas) and Tables are eligible for use with your connector. You can find examples of this in the + * athena-hbase and athena-docdb connector modules. A common reason for this is when you happen to have databases/tables + * in Glue which match the names of databases and tables in your source but that aren't actually relevant. In such cases + * you may choose to ignore those Glue tables. + *

+ * At present this class does not retrieve partition information from AWS Glue's DataCatalog. There is an open task + * for how best to handle partitioning information in this class: https://github.com/awslabs/aws-athena-query-federation/issues/5 + * It is unclear at this time how many sources will have meaningful partition info in Glue but many sources (DocDB, Hbase, Redis) + * benefited from having basic schema information in Glue. As a result we punted support for partition information to + * a later time. + * + * @note All schema names, table names, and column names must be lower case at this time. Any entities that are uppercase or + * mixed case will not be accessible in queries and will be lower cased by Athena's engine to ensure consistency across + * sources. As such you may need to handle this when integrating with a source that supports mixed case. As an example, + * you can look at the CloudwatchTableResolver in the athena-cloudwatch module for one potential approach to this challenge. + * @see MetadataHandler + */ +public abstract class GlueMetadataHandler + extends MetadataHandler +{ + //name of the environment variable that can be used to set which Glue catalog to use (e.g. setting this to + //a different aws account id allows you to use cross-account catalogs) + private static final String CATALOG_NAME_ENV_OVERRIDE = "glue_catalog"; + + private final AWSGlue awsGlue; + + /** + * Basic constructor which is recommended when extending this class. + * + * @param awsGlue The glue client to use. + * @param sourceType The source type, used in diagnostic logging. + */ + public GlueMetadataHandler(AWSGlue awsGlue, String sourceType) + { + super(sourceType); + this.awsGlue = awsGlue; + } + + /** + * Full DI constructor used mostly for testing + * + * @param awsGlue The glue client to use. + * @param encryptionKeyFactory The EncryptionKeyFactory to use for spill encryption. + * @param secretsManager The AWSSecretsManager client that can be used when attempting to resolve secrets. + * @param athena The Athena client that can be used to fetch query termination status to fast-fail this handler. + * @param spillBucket The S3 Bucket to use when spilling results. + * @param spillPrefix The S3 prefix to use when spilling results. + */ + @VisibleForTesting + protected GlueMetadataHandler(AWSGlue awsGlue, + EncryptionKeyFactory encryptionKeyFactory, + AWSSecretsManager secretsManager, + AmazonAthena athena, + String sourceType, + String spillBucket, + String spillPrefix) + { + super(encryptionKeyFactory, secretsManager, athena, sourceType, spillBucket, spillPrefix); + this.awsGlue = awsGlue; + } + + /** + * Provides access to the Glue client if the extender should need it. + * + * @return The AWSGlue client being used by this class. + */ + protected AWSGlue getAwsGlue() + { + return awsGlue; + } + + /** + * Provides access to the current AWS Glue DataCatalog being used by this class. + * + * @param request The request for which we'd like to resolve the catalog. + * @return The glue catalog to use for the request. + */ + protected String getCatalog(MetadataRequest request) + { + String override = System.getenv(CATALOG_NAME_ENV_OVERRIDE); + if (override == null) { + return request.getIdentity().getAccount(); + } + return override; + } + + /** + * Returns an unfiltered list of schemas (aka databases) from AWS Glue DataCatalog. + * + * @param blockAllocator Tool for creating and managing Apache Arrow Blocks. + * @param request Provides details on who made the request and which Athena catalog they are querying. + * @return The ListSchemasResponse which mostly contains the list of schemas (aka databases). + */ + @Override + public ListSchemasResponse doListSchemaNames(BlockAllocator blockAllocator, ListSchemasRequest request) + throws Exception + { + return doListSchemaNames(blockAllocator, request, null); + } + + /** + * Returns a list of schemas (aka databases) from AWS Glue DataCatalog with optional filtering. + * + * @param blockAllocator Tool for creating and managing Apache Arrow Blocks. + * @param request Provides details on who made the request and which Athena catalog they are querying. + * @param filter The DatabaseFilter to apply to all schemas (aka databases) before adding them to the results list. + * @return The ListSchemasResponse which mostly contains the list of schemas (aka databases). + */ + protected ListSchemasResponse doListSchemaNames(BlockAllocator blockAllocator, ListSchemasRequest request, DatabaseFilter filter) + throws Exception + { + GetDatabasesRequest getDatabasesRequest = new GetDatabasesRequest(); + getDatabasesRequest.setCatalogId(getCatalog(request)); + + List schemas = new ArrayList<>(); + String nextToken = null; + do { + getDatabasesRequest.setNextToken(nextToken); + GetDatabasesResult result = awsGlue.getDatabases(getDatabasesRequest); + + for (Database next : result.getDatabaseList()) { + if (filter == null || filter.filter(next)) { + schemas.add(next.getName()); + } + } + + nextToken = result.getNextToken(); + } + while (nextToken != null); + + return new ListSchemasResponse(request.getCatalogName(), schemas); + } + + /** + * Returns an unfiltered list of tables from AWS Glue DataCatalog for the requested schema (aka database) + * + * @param blockAllocator Tool for creating and managing Apache Arrow Blocks. + * @param request Provides details on who made the request and which Athena catalog they are querying. + * @return The ListTablesResponse which mostly contains the list of table names. + */ + @Override + public ListTablesResponse doListTables(BlockAllocator blockAllocator, ListTablesRequest request) + throws Exception + { + return doListTables(blockAllocator, request, null); + } + + /** + * Returns a list of tables from AWS Glue DataCatalog with optional filtering for the requested schema (aka database) + * + * @param blockAllocator Tool for creating and managing Apache Arrow Blocks. + * @param request Provides details on who made the request and which Athena catalog they are querying. + * @param filter The TableFilter to apply to all tables before adding them to the results list. + * @return The ListTablesResponse which mostly contains the list of table names. + */ + protected ListTablesResponse doListTables(BlockAllocator blockAllocator, ListTablesRequest request, TableFilter filter) + throws Exception + { + GetTablesRequest getTablesRequest = new GetTablesRequest(); + getTablesRequest.setCatalogId(getCatalog(request)); + getTablesRequest.setDatabaseName(request.getSchemaName()); + + Set tables = new HashSet<>(); + String nextToken = null; + do { + getTablesRequest.setNextToken(nextToken); + GetTablesResult result = awsGlue.getTables(getTablesRequest); + + for (Table next : result.getTableList()) { + if (filter == null || filter.filter(next)) { + tables.add(new TableName(request.getSchemaName(), next.getName())); + } + } + + nextToken = result.getNextToken(); + } + while (nextToken != null); + + return new ListTablesResponse(request.getCatalogName(), tables); + } + + /** + * Attempts to retrieve a Table (columns and properties) from AWS Glue for the request schema (aka database) and table + * name with no fitlering. + * + * @param blockAllocator Tool for creating and managing Apache Arrow Blocks. + * @param request Provides details on who made the request and which Athena catalog, database, and table they are querying. + * @return A GetTableResponse mostly containing the columns, their types, and any table properties for the requested table. + */ + @Override + public GetTableResponse doGetTable(BlockAllocator blockAllocator, GetTableRequest request) + throws Exception + { + return doGetTable(blockAllocator, request, null); + } + + /** + * Attempts to retrieve a Table (columns and properties) from AWS Glue for the request schema (aka database) and table + * name with no filtering. + * + * @param blockAllocator Tool for creating and managing Apache Arrow Blocks. + * @param request Provides details on who made the request and which Athena catalog, database, and table they are querying. + * @param filter The TableFilter to apply to any matching table before generating the result. + * @return A GetTableResponse mostly containing the columns, their types, and any table properties for the requested table. + * @note This method throws a RuntimeException if not table matching the requested criteria (and filter) is found. + */ + protected GetTableResponse doGetTable(BlockAllocator blockAllocator, GetTableRequest request, TableFilter filter) + throws Exception + { + TableName tableName = request.getTableName(); + com.amazonaws.services.glue.model.GetTableRequest getTableRequest = new com.amazonaws.services.glue.model.GetTableRequest(); + getTableRequest.setCatalogId(getCatalog(request)); + getTableRequest.setDatabaseName(tableName.getSchemaName()); + getTableRequest.setName(tableName.getTableName()); + + GetTableResult result = awsGlue.getTable(getTableRequest); + Table table = result.getTable(); + + if (filter != null && !filter.filter(table)) { + throw new RuntimeException("No matching table found " + request.getTableName()); + } + + SchemaBuilder schemaBuilder = SchemaBuilder.newBuilder(); + table.getParameters().entrySet().forEach(next -> schemaBuilder.addMetadata(next.getKey(), next.getValue())); + + Set partitionCols = table.getPartitionKeys() + .stream().map(next -> next.getName()).collect(Collectors.toSet()); + + for (Column next : table.getStorageDescriptor().getColumns()) { + schemaBuilder.addField(convertField(next.getName(), next.getType())); + if (next.getComment() != null) { + schemaBuilder.addMetadata(next.getName(), next.getComment()); + } + } + + return new GetTableResponse(request.getCatalogName(), + request.getTableName(), + schemaBuilder.build(), + partitionCols); + } + + protected Field convertField(String name, String glueType) + { + return GlueFieldLexer.lex(name, glueType); + } + + public interface TableFilter + { + /** + * Used to filter table results. + * + * @param table The table to evaluate. + * @return True if the provided table should be in the result, False if not. + */ + boolean filter(Table table); + } + + public interface DatabaseFilter + { + /** + * Used to filter database results. + * + * @param database The database to evaluate. + * @return True if the provided database should be in the result, False if not. + */ + boolean filter(Database database); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/handlers/MetadataHandler.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/handlers/MetadataHandler.java new file mode 100644 index 0000000000..e504d2a797 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/handlers/MetadataHandler.java @@ -0,0 +1,457 @@ +package com.amazonaws.athena.connector.lambda.handlers; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.ThrottlingInvoker; +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.amazonaws.athena.connector.lambda.data.BlockUtils; +import com.amazonaws.athena.connector.lambda.data.BlockWriter; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.data.SimpleBlockWriter; +import com.amazonaws.athena.connector.lambda.domain.predicate.ConstraintEvaluator; +import com.amazonaws.athena.connector.lambda.domain.spill.S3SpillLocation; +import com.amazonaws.athena.connector.lambda.domain.spill.SpillLocation; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesResponse; +import com.amazonaws.athena.connector.lambda.metadata.MetadataRequest; +import com.amazonaws.athena.connector.lambda.metadata.MetadataRequestType; +import com.amazonaws.athena.connector.lambda.request.FederationRequest; +import com.amazonaws.athena.connector.lambda.request.FederationResponse; +import com.amazonaws.athena.connector.lambda.request.PingRequest; +import com.amazonaws.athena.connector.lambda.request.PingResponse; +import com.amazonaws.athena.connector.lambda.security.CachableSecretsManager; +import com.amazonaws.athena.connector.lambda.security.EncryptionKey; +import com.amazonaws.athena.connector.lambda.security.EncryptionKeyFactory; +import com.amazonaws.athena.connector.lambda.security.KmsKeyFactory; +import com.amazonaws.athena.connector.lambda.security.LocalKeyFactory; +import com.amazonaws.athena.connector.lambda.serde.ObjectMapperFactory; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.athena.AmazonAthenaClientBuilder; +import com.amazonaws.services.kms.AWSKMSClientBuilder; +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestStreamHandler; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.amazonaws.services.secretsmanager.AWSSecretsManagerClientBuilder; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.arrow.vector.types.pojo.Schema; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.UUID; + +import static com.amazonaws.athena.connector.lambda.handlers.AthenaExceptionFilter.ATHENA_EXCEPTION_FILTER; +import static com.amazonaws.athena.connector.lambda.handlers.FederationCapabilities.CAPABILITIES; + +/** + * This class defines the functionality required by any valid source of federated metadata for Athena. It is recommended + * that all connectors extend this class for Metadata operations though it is possible for you to write your own + * from the ground up as long as you satisfy the wire protocol. For all cases we've encountered it has made more sense + * to start with this base class and use it's implementation for most of the boilerplate related to Lambda and resource + * lifecycle so we could focus on the task of integrating with the source we were interested in. + * + * @note All schema names, table names, and column names must be lower case at this time. Any entities that are uppercase or + * mixed case will not be accessible in queries and will be lower cased by Athena's engine to ensure consistency across + * sources. As such you may need to handle this when integrating with a source that supports mixed case. As an example, + * you can look at the CloudwatchTableResolver in the athena-cloudwatch module for one potential approach to this challenge. + */ +public abstract class MetadataHandler + implements RequestStreamHandler +{ + private static final Logger logger = LoggerFactory.getLogger(MetadataHandler.class); + //name of the default column used when a default single-partition response is required for connectors that + //do not support robust partitioning. In such cases Athena requires at least 1 partition in order indicate + //there is indeed data to be read vs. queries that were able to fully partition prune and thus decide there + //was no data to read. + private static final String PARTITION_ID_COL = "partitionId"; + //The value that denotes encryption should be disabled, encryption is enabled by default. + private static final String DISABLE_ENCRYPTION = "true"; + //The default S3 prefix to use when spilling to S3 + private static final String DEFAULT_SPILL_PREFIX = "athena-federation-spill"; + protected static final String SPILL_BUCKET_ENV = "spill_bucket"; + protected static final String SPILL_PREFIX_ENV = "spill_prefix"; + protected static final String KMS_KEY_ID_ENV = "kms_key_id"; + protected static final String DISABLE_SPILL_ENCRYPTION = "disable_spill_encryption"; + + private final CachableSecretsManager secretsManager; + private final AmazonAthena athena; + private final ThrottlingInvoker athenaInvoker = ThrottlingInvoker.newDefaultBuilder(ATHENA_EXCEPTION_FILTER).build(); + private final EncryptionKeyFactory encryptionKeyFactory; + private final String spillBucket; + private final String spillPrefix; + private final String sourceType; + + /** + * @param sourceType Used to aid in logging diagnostic info when raising a support case. + */ + public MetadataHandler(String sourceType) + { + this.sourceType = sourceType; + this.spillBucket = System.getenv(SPILL_BUCKET_ENV); + this.spillPrefix = System.getenv(SPILL_PREFIX_ENV) == null ? + DEFAULT_SPILL_PREFIX : System.getenv(SPILL_PREFIX_ENV); + if (System.getenv(DISABLE_SPILL_ENCRYPTION) == null || + !DISABLE_ENCRYPTION.equalsIgnoreCase(System.getenv(DISABLE_SPILL_ENCRYPTION))) { + encryptionKeyFactory = (System.getenv(KMS_KEY_ID_ENV) != null) ? + new KmsKeyFactory(AWSKMSClientBuilder.standard().build(), System.getenv(KMS_KEY_ID_ENV)) : + new LocalKeyFactory(); + } + else { + encryptionKeyFactory = null; + } + + this.secretsManager = new CachableSecretsManager(AWSSecretsManagerClientBuilder.defaultClient()); + this.athena = AmazonAthenaClientBuilder.defaultClient(); + } + + /** + * @param sourceType Used to aid in logging diagnostic info when raising a support case. + */ + public MetadataHandler(EncryptionKeyFactory encryptionKeyFactory, + AWSSecretsManager secretsManager, + AmazonAthena athena, + String sourceType, + String spillBucket, + String spillPrefix) + { + this.encryptionKeyFactory = encryptionKeyFactory; + this.secretsManager = new CachableSecretsManager(secretsManager); + this.athena = athena; + this.sourceType = sourceType; + this.spillBucket = spillBucket; + this.spillPrefix = spillPrefix; + } + + /** + * Resolves any secrets found in the supplied string, for example: MyString${WithSecret} would have ${WithSecret} + * by the corresponding value of the secret in AWS Secrets Manager with that name. If no such secret is found + * the function throws. + * + * @param rawString The string in which you'd like to replace SecretsManager placeholders. + * (e.g. ThisIsA${Secret}Here - The ${Secret} would be replaced with the contents of an SecretsManager + * secret called Secret. If no such secret is found, the function throws. If no ${} are found in + * the input string, nothing is replaced and the original string is returned. + */ + protected String resolveSecrets(String rawString) + { + return secretsManager.resolveSecrets(rawString); + } + + protected String getSecret(String secretName) + { + return secretsManager.getSecret(secretName); + } + + protected EncryptionKey makeEncryptionKey() + { + return (encryptionKeyFactory != null) ? encryptionKeyFactory.create() : null; + } + + protected SpillLocation makeSpillLocation(MetadataRequest request) + { + return S3SpillLocation.newBuilder() + .withBucket(spillBucket) + .withPrefix(spillPrefix) + .withQueryId(request.getQueryId()) + .withSplitId(UUID.randomUUID().toString()) + .build(); + } + + public final void handleRequest(InputStream inputStream, OutputStream outputStream, final Context context) + throws IOException + { + try (BlockAllocator allocator = new BlockAllocatorImpl()) { + ObjectMapper objectMapper = ObjectMapperFactory.create(allocator); + try (FederationRequest rawReq = objectMapper.readValue(inputStream, FederationRequest.class)) { + if (rawReq instanceof PingRequest) { + try (PingResponse response = doPing((PingRequest) rawReq)) { + assertNotNull(response); + objectMapper.writeValue(outputStream, response); + } + return; + } + + if (!(rawReq instanceof MetadataRequest)) { + throw new RuntimeException("Expected a MetadataRequest but found " + rawReq.getClass()); + } + doHandleRequest(allocator, objectMapper, (MetadataRequest) rawReq, outputStream); + } + catch (Exception ex) { + logger.warn("handleRequest: Completed with an exception.", ex); + throw (ex instanceof RuntimeException) ? (RuntimeException) ex : new RuntimeException(ex); + } + } + } + + protected final void doHandleRequest(BlockAllocator allocator, + ObjectMapper objectMapper, + MetadataRequest req, + OutputStream outputStream) + throws Exception + { + logger.info("doHandleRequest: request[{}]", req); + MetadataRequestType type = req.getRequestType(); + switch (type) { + case LIST_SCHEMAS: + try (ListSchemasResponse response = doListSchemaNames(allocator, (ListSchemasRequest) req)) { + logger.info("doHandleRequest: response[{}]", response); + assertNotNull(response); + objectMapper.writeValue(outputStream, response); + } + return; + case LIST_TABLES: + try (ListTablesResponse response = doListTables(allocator, (ListTablesRequest) req)) { + logger.info("doHandleRequest: response[{}]", response); + assertNotNull(response); + objectMapper.writeValue(outputStream, response); + } + return; + case GET_TABLE: + try (GetTableResponse response = doGetTable(allocator, (GetTableRequest) req)) { + logger.info("doHandleRequest: response[{}]", response); + assertNotNull(response); + objectMapper.writeValue(outputStream, response); + } + return; + case GET_TABLE_LAYOUT: + try (GetTableLayoutResponse response = doGetTableLayout(allocator, (GetTableLayoutRequest) req)) { + logger.info("doHandleRequest: response[{}]", response); + assertNotNull(response); + objectMapper.writeValue(outputStream, response); + } + return; + case GET_SPLITS: + try (GetSplitsResponse response = doGetSplits(allocator, (GetSplitsRequest) req)) { + logger.info("doHandleRequest: response[{}]", response); + assertNotNull(response); + objectMapper.writeValue(outputStream, response); + } + return; + default: + throw new IllegalArgumentException("Unknown request type " + type); + } + } + + /** + * Used to get the list of schemas (aka databases) that this source contains. + * + * @param allocator Tool for creating and managing Apache Arrow Blocks. + * @param request Provides details on who made the request and which Athena catalog they are querying. + * @return A ListSchemasResponse which primarily contains a Set of schema names and a catalog name + * corresponding the Athena catalog that was queried. + */ + public abstract ListSchemasResponse doListSchemaNames(final BlockAllocator allocator, final ListSchemasRequest request) + throws Exception; + + /** + * Used to get the list of tables that this source contains. + * + * @param allocator Tool for creating and managing Apache Arrow Blocks. + * @param request Provides details on who made the request and which Athena catalog and database they are querying. + * @return A ListTablesResponse which primarily contains a List enumerating the tables in this + * catalog, database tuple. It also contains the catalog name corresponding the Athena catalog that was queried. + */ + public abstract ListTablesResponse doListTables(final BlockAllocator allocator, final ListTablesRequest request) + throws Exception; + + /** + * Used to get definition (field names, types, descriptions, etc...) of a Table. + * + * @param allocator Tool for creating and managing Apache Arrow Blocks. + * @param request Provides details on who made the request and which Athena catalog, database, and table they are querying. + * @return A GetTableResponse which primarily contains: + * 1. An Apache Arrow Schema object describing the table's columns, types, and descriptions. + * 2. A Set of partition column names (or empty if the table isn't partitioned). + */ + public abstract GetTableResponse doGetTable(final BlockAllocator allocator, final GetTableRequest request) + throws Exception; + + /** + * Used to get the partitions that must be read from the request table in order to satisfy the requested predicate. + * + * @param allocator Tool for creating and managing Apache Arrow Blocks. + * @param request Provides details of the catalog, database, and table being queried as well as any filter predicate. + * @return A GetTableLayoutResponse which primarily contains: + * 1. An Apache Arrow Block with 0 or more partitions to read. 0 partitions implies there are 0 rows to read. + * 2. Set of partition column names which should correspond to columns in your Apache Arrow Block. + * @note Partitions are opaque to Amazon Athena in that it does not understand their contents, just that it must call + * doGetSplits(...) for each partition you return in order to determine which reads to perform and if those reads + * can be parallelized. This means the contents of this response are more for you than they are for Athena. + * @note Partitions are partially opaque to Amazon Athena in that it only understands your partition columns and + * how to filter out partitions that do not meet the query's constraints. Any additional columns you add to the + * partition data are ignored by Athena but passed on to calls on GetSplits. + */ + public GetTableLayoutResponse doGetTableLayout(final BlockAllocator allocator, final GetTableLayoutRequest request) + throws Exception + { + SchemaBuilder constraintSchema = new SchemaBuilder().newBuilder(); + SchemaBuilder partitionSchemaBuilder = new SchemaBuilder().newBuilder(); + + /** + * Add our partition columns to the response schema so the engine knows how to interpret the list of + * partitions we are going to return. + */ + for (String nextPartCol : request.getPartitionCols()) { + Field partitionCol = request.getSchema().findField(nextPartCol); + partitionSchemaBuilder.addField(nextPartCol, partitionCol.getType()); + constraintSchema.addField(nextPartCol, partitionCol.getType()); + } + + enhancePartitionSchema(partitionSchemaBuilder, request); + Schema partitionSchema = partitionSchemaBuilder.build(); + + if (partitionSchema.getFields().isEmpty() && partitionSchema.getCustomMetadata().isEmpty()) { + //Even though our table doesn't support complex layouts, partitioning or metadata, we need to convey that there is at least + //1 partition to read as part of the query or Athena will assume partition pruning found no candidate layouts to read. + Block partitions = BlockUtils.newBlock(allocator, PARTITION_ID_COL, Types.MinorType.INT.getType(), 1); + return new GetTableLayoutResponse(request.getCatalogName(), request.getTableName(), partitions); + } + + /** + * Now use the constraint that was in the request to do some partition pruning. Here we are just + * generating some fake values for the partitions but in a real implementation you'd use your metastore + * or knowledge of the actual table's physical layout to do this. + */ + try (ConstraintEvaluator constraintEvaluator = new ConstraintEvaluator(allocator, + constraintSchema.build(), + request.getConstraints()); + QueryStatusChecker queryStatusChecker = new QueryStatusChecker(athena, athenaInvoker, request.getQueryId()) + ) { + Block partitions = allocator.createBlock(partitionSchemaBuilder.build()); + partitions.constrain(constraintEvaluator); + SimpleBlockWriter blockWriter = new SimpleBlockWriter(partitions); + getPartitions(blockWriter, request, queryStatusChecker); + return new GetTableLayoutResponse(request.getCatalogName(), request.getTableName(), partitions); + } + } + + /** + * This method can be used to add additional fields to the schema of our partition response. Athena + * expects each partitions in the response to have a column corresponding to your partition columns. + * You can choose to add additional columns to that response which Athena will ignore but will pass + * on to you when it call GetSplits(...) for each partition. + * + * @param partitionSchemaBuilder The SchemaBuilder you can use to add additional columns and metadata to the + * partitions response. + * @param request The GetTableLayoutResquest that triggered this call. + */ + public void enhancePartitionSchema(SchemaBuilder partitionSchemaBuilder, GetTableLayoutRequest request) + { + //You can add additional fields to the partition schema which are ignored by Athena + //but will be passed on to called to GetSplits(...). This can be handy when you + //want to avoid extra round trips to your metastore. For example, when you generate + //the partition list you may have easy access to the storage details (e.g. S3 location) + //of the partition. Athena doesn't need the S3 location but when Athena calls you + //to generate the Splits for the partition, having the S3 location would save you + //extra work. For that reason you can add a field to the partition schema which + //contains the s3 location. + } + + /** + * Used to get the partitions that must be read from the request table in order to satisfy the requested predicate. + * + * @param blockWriter Used to write rows (partitions) into the Apache Arrow response. + * @param request Provides details of the catalog, database, and table being queried as well as any filter predicate. + * @param queryStatusChecker A QueryStatusChecker that you can use to stop doing work for a query that has already terminated + * @note Partitions are partially opaque to Amazon Athena in that it only understands your partition columns and + * how to filter out partitions that do not meet the query's constraints. Any additional columns you add to the + * partition data are ignored by Athena but passed on to calls on GetSplits. Also note tat the BlockWriter handlers + * automatically constraining and filtering out values that don't satisfy the query's predicate. This is how we + * we accomplish partition pruning. You can optionally retreive a ConstraintEvaluator from BlockWriter if you have + * your own need to apply filtering in Lambda. Otherwise you can get the actual preducate from the request object + * for pushing down into the source you are querying. + */ + public abstract void getPartitions(final BlockWriter blockWriter, + final GetTableLayoutRequest request, QueryStatusChecker queryStatusChecker) + throws Exception; + + /** + * Used to split-up the reads required to scan the requested batch of partition(s). + * + * @param allocator Tool for creating and managing Apache Arrow Blocks. + * @param request Provides details of the catalog, database, table, andpartition(s) being queried as well as + * any filter predicate. + * @return A GetSplitsResponse which primarily contains: + * 1. A Set which represent read operations Amazon Athena must perform by calling your read function. + * 2. (Optional) A continuation token which allows you to paginate the generation of splits for large queries. + * @note A Split is a mostly opaque object to Amazon Athena. Amazon Athena will use the optional SpillLocation and + * optional EncryptionKey for pipelined reads but all properties you set on the Split are passed to your read + * function to help you perform the read. + */ + public abstract GetSplitsResponse doGetSplits(BlockAllocator allocator, GetSplitsRequest request) + throws Exception; + + /** + * Used to warm up your function as well as to discovery its capabilities (e.g. SDK capabilities) + * + * @param request The PingRequest. + * @return A PingResponse. + * @note We do not recommend modifying this function, instead you should implement doPing(...) + */ + public PingResponse doPing(PingRequest request) + { + PingResponse response = new PingResponse(request.getCatalogName(), request.getQueryId(), sourceType, CAPABILITIES); + try { + onPing(request); + } + catch (Exception ex) { + logger.warn("doPing: encountered an exception while delegating onPing.", ex); + } + return response; + } + + /** + * Provides you a signal that can be used to warm up your function. + * + * @param request The PingRequest. + */ + public void onPing(PingRequest request) + { + //NoOp + } + + /** + * Helper function that is used to ensure we always have a non-null response. + * + * @param response The response to assert is not null. + */ + private void assertNotNull(FederationResponse response) + { + if (response == null) { + throw new RuntimeException("Response was null"); + } + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/handlers/RecordHandler.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/handlers/RecordHandler.java new file mode 100644 index 0000000000..0a97becaa5 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/handlers/RecordHandler.java @@ -0,0 +1,260 @@ +package com.amazonaws.athena.connector.lambda.handlers; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.ThrottlingInvoker; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.amazonaws.athena.connector.lambda.data.BlockSpiller; +import com.amazonaws.athena.connector.lambda.data.S3BlockSpiller; +import com.amazonaws.athena.connector.lambda.data.SpillConfig; +import com.amazonaws.athena.connector.lambda.domain.predicate.ConstraintEvaluator; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsResponse; +import com.amazonaws.athena.connector.lambda.records.RecordRequest; +import com.amazonaws.athena.connector.lambda.records.RecordRequestType; +import com.amazonaws.athena.connector.lambda.records.RecordResponse; +import com.amazonaws.athena.connector.lambda.records.RemoteReadRecordsResponse; +import com.amazonaws.athena.connector.lambda.request.FederationRequest; +import com.amazonaws.athena.connector.lambda.request.FederationResponse; +import com.amazonaws.athena.connector.lambda.request.PingRequest; +import com.amazonaws.athena.connector.lambda.request.PingResponse; +import com.amazonaws.athena.connector.lambda.security.CachableSecretsManager; +import com.amazonaws.athena.connector.lambda.serde.ObjectMapperFactory; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.athena.AmazonAthenaClientBuilder; +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestStreamHandler; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.amazonaws.services.secretsmanager.AWSSecretsManagerClientBuilder; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import static com.amazonaws.athena.connector.lambda.handlers.AthenaExceptionFilter.ATHENA_EXCEPTION_FILTER; +import static com.amazonaws.athena.connector.lambda.handlers.FederationCapabilities.CAPABILITIES; + +public abstract class RecordHandler + implements RequestStreamHandler +{ + private static final Logger logger = LoggerFactory.getLogger(RecordHandler.class); + private static final String MAX_BLOCK_SIZE_BYTES = "MAX_BLOCK_SIZE_BYTES"; + private static final int NUM_SPILL_THREADS = 2; + private final AmazonS3 amazonS3; + private final String sourceType; + private final CachableSecretsManager secretsManager; + private final AmazonAthena athena; + private final ThrottlingInvoker athenaInvoker = ThrottlingInvoker.newDefaultBuilder(ATHENA_EXCEPTION_FILTER).build(); + + /** + * @param sourceType Used to aid in logging diagnostic info when raising a support case. + */ + public RecordHandler(String sourceType) + { + this.sourceType = sourceType; + this.amazonS3 = AmazonS3ClientBuilder.defaultClient(); + this.secretsManager = new CachableSecretsManager(AWSSecretsManagerClientBuilder.defaultClient()); + this.athena = AmazonAthenaClientBuilder.defaultClient(); + } + + /** + * @param sourceType Used to aid in logging diagnostic info when raising a support case. + */ + public RecordHandler(AmazonS3 amazonS3, AWSSecretsManager secretsManager, AmazonAthena athena, String sourceType) + { + this.sourceType = sourceType; + this.amazonS3 = amazonS3; + this.secretsManager = new CachableSecretsManager(secretsManager); + this.athena = athena; + } + + /** + * Resolves any secrets found in the supplied string, for example: MyString${WithSecret} would have ${WithSecret} + * by the corresponding value of the secret in AWS Secrets Manager with that name. If no such secret is found + * the function throws. + * + * @param rawString The string in which you'd like to replace SecretsManager placeholders. + * (e.g. ThisIsA${Secret}Here - The ${Secret} would be replaced with the contents of an SecretsManager + * secret called Secret. If no such secret is found, the function throws. If no ${} are found in + * the input string, nothing is replaced and the original string is returned. + */ + protected String resolveSecrets(String rawString) + { + return secretsManager.resolveSecrets(rawString); + } + + protected String getSecret(String secretName) + { + return secretsManager.getSecret(secretName); + } + + public final void handleRequest(InputStream inputStream, OutputStream outputStream, final Context context) + throws IOException + { + try (BlockAllocator allocator = new BlockAllocatorImpl()) { + ObjectMapper objectMapper = ObjectMapperFactory.create(allocator); + try (FederationRequest rawReq = objectMapper.readValue(inputStream, FederationRequest.class)) { + if (rawReq instanceof PingRequest) { + try (PingResponse response = doPing((PingRequest) rawReq)) { + assertNotNull(response); + objectMapper.writeValue(outputStream, response); + } + return; + } + + if (!(rawReq instanceof RecordRequest)) { + throw new RuntimeException("Expected a RecordRequest but found " + rawReq.getClass()); + } + + doHandleRequest(allocator, objectMapper, (RecordRequest) rawReq, outputStream); + } + catch (Exception ex) { + logger.warn("handleRequest: Completed with an exception.", ex); + throw (ex instanceof RuntimeException) ? (RuntimeException) ex : new RuntimeException(ex); + } + } + } + + protected final void doHandleRequest(BlockAllocator allocator, + ObjectMapper objectMapper, + RecordRequest req, + OutputStream outputStream) + throws Exception + { + logger.info("doHandleRequest: request[{}]", req); + RecordRequestType type = req.getRequestType(); + switch (type) { + case READ_RECORDS: + try (RecordResponse response = doReadRecords(allocator, (ReadRecordsRequest) req)) { + logger.info("doHandleRequest: response[{}]", response); + assertNotNull(response); + objectMapper.writeValue(outputStream, response); + } + return; + default: + throw new IllegalArgumentException("Unknown request type " + type); + } + } + + /** + * Used to read the row data associated with the provided Split. + * + * @param allocator Tool for creating and managing Apache Arrow Blocks. + * @param request Details of the read request, including: + * 1. The Split + * 2. The Catalog, Database, and Table the read request is for. + * 3. The filtering predicate (if any) + * 4. The columns required for projection. + * @return A RecordResponse which either a ReadRecordsResponse or a RemoteReadRecordsResponse containing the row + * data for the requested Split. + */ + public RecordResponse doReadRecords(BlockAllocator allocator, ReadRecordsRequest request) + throws Exception + { + logger.info("doReadRecords: {}:{}", request.getSchema(), request.getSplit().getSpillLocation()); + SpillConfig spillConfig = getSpillConfig(request); + try (ConstraintEvaluator evaluator = new ConstraintEvaluator(allocator, + request.getSchema(), + request.getConstraints()); + S3BlockSpiller spiller = new S3BlockSpiller(amazonS3, spillConfig, allocator, request.getSchema(), evaluator); + QueryStatusChecker queryStatusChecker = new QueryStatusChecker(athena, athenaInvoker, request.getQueryId()) + ) { + readWithConstraint(spiller, request, queryStatusChecker); + + if (!spiller.spilled()) { + return new ReadRecordsResponse(request.getCatalogName(), spiller.getBlock()); + } + else { + return new RemoteReadRecordsResponse(request.getCatalogName(), + request.getSchema(), + spiller.getSpillLocations(), + spillConfig.getEncryptionKey()); + } + } + } + + /** + * A more stream lined option for reading the row data associated with the provided Split. This method differs from + * doReadRecords(...) in that the SDK handles more of the request lifecycle, leaving you to focus more closely on + * the task of actually reading from your source. + * + * @param spiller A BlockSpiller that should be used to write the row data associated with this Split. + * The BlockSpiller automatically handles chunking the response, encrypting, and spilling to S3. + * @param recordsRequest Details of the read request, including: + * 1. The Split + * 2. The Catalog, Database, and Table the read request is for. + * 3. The filtering predicate (if any) + * 4. The columns required for projection. + * @param queryStatusChecker A QueryStatusChecker that you can use to stop doing work for a query that has already terminated + * @note Avoid writing >10 rows per-call to BlockSpiller.writeRow(...) because this will limit the BlockSpiller's + * ability to control Block size. The resulting increase in Block size may cause failures and reduced performance. + */ + protected abstract void readWithConstraint(BlockSpiller spiller, ReadRecordsRequest recordsRequest, QueryStatusChecker queryStatusChecker) + throws Exception; + + protected SpillConfig getSpillConfig(ReadRecordsRequest request) + { + long maxBlockSize = request.getMaxBlockSize(); + if (System.getenv(MAX_BLOCK_SIZE_BYTES) != null) { + maxBlockSize = Long.parseLong(System.getenv(MAX_BLOCK_SIZE_BYTES)); + } + + return SpillConfig.newBuilder() + .withSpillLocation(request.getSplit().getSpillLocation()) + .withMaxBlockBytes(maxBlockSize) + .withMaxInlineBlockBytes(request.getMaxInlineBlockSize()) + .withRequestId(request.getQueryId()) + .withEncryptionKey(request.getSplit().getEncryptionKey()) + .withNumSpillThreads(NUM_SPILL_THREADS) + .build(); + } + + private final PingResponse doPing(PingRequest request) + { + PingResponse response = new PingResponse(request.getCatalogName(), request.getQueryId(), sourceType, CAPABILITIES); + try { + onPing(request); + } + catch (Exception ex) { + logger.warn("doPing: encountered an exception while delegating onPing.", ex); + } + return response; + } + + protected void onPing(PingRequest request) + { + //NoOp + } + + private void assertNotNull(FederationResponse response) + { + if (response == null) { + throw new RuntimeException("Response was null"); + } + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/GetSplitsRequest.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/GetSplitsRequest.java new file mode 100644 index 0000000000..c6451830e3 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/GetSplitsRequest.java @@ -0,0 +1,174 @@ +package com.amazonaws.athena.connector.lambda.metadata; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.CollectionsUtils; +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.security.FederatedIdentity; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.MoreObjects; +import com.google.common.base.Objects; +import org.apache.arrow.vector.types.pojo.Schema; + +import java.beans.Transient; +import java.util.Collections; +import java.util.List; + +import static java.util.Objects.requireNonNull; + +public class GetSplitsRequest + extends MetadataRequest +{ + private final TableName tableName; + private final Block partitions; + private final List partitionCols; + private final Constraints constraints; + private final String continuationToken; + + @JsonCreator + public GetSplitsRequest(@JsonProperty("identity") FederatedIdentity identity, + @JsonProperty("queryId") String queryId, + @JsonProperty("catalogName") String catalogName, + @JsonProperty("tableName") TableName tableName, + @JsonProperty("partitions") Block partitions, + @JsonProperty("partitionCols") List partitionCols, + @JsonProperty("constraints") Constraints constraints, + @JsonProperty("continuationToken") String continuationToken) + { + super(identity, MetadataRequestType.GET_SPLITS, queryId, catalogName); + requireNonNull(tableName, "tableName is null"); + requireNonNull(partitions, "partitions is null"); + requireNonNull(partitionCols, "partitionCols is null"); + requireNonNull(constraints, "constraints is null"); + this.tableName = tableName; + this.partitions = partitions; + this.partitionCols = Collections.unmodifiableList(partitionCols); + this.constraints = constraints; + this.continuationToken = continuationToken; + } + + //Helpful when making a continuation call since it requires the original request but updated token + public GetSplitsRequest(GetSplitsRequest clone, String continuationToken) + { + this(clone.getIdentity(), clone.getQueryId(), clone.getCatalogName(), clone.tableName, clone.partitions, clone.partitionCols, clone.constraints, continuationToken); + } + + @JsonProperty + public String getContinuationToken() + { + return continuationToken; + } + + @JsonProperty + public TableName getTableName() + { + return tableName; + } + + @Transient + public Schema getSchema() + { + return partitions.getSchema(); + } + + @JsonProperty + public List getPartitionCols() + { + return partitionCols; + } + + @JsonProperty + public Block getPartitions() + { + return partitions; + } + + @JsonProperty + public Constraints getConstraints() + { + return constraints; + } + + @Transient + public boolean hasContinuationToken() + { + return continuationToken != null; + } + + @Override + public String toString() + { + return MoreObjects.toStringHelper(this) + .add("queryId", getQueryId()) + .add("tableName", tableName) + .add("partitionCols", partitionCols) + .add("requestType", getRequestType()) + .add("catalogName", getCatalogName()) + .add("partitions", partitions) + .add("constraints", constraints) + .add("continuationToken", continuationToken) + .toString(); + } + + @Override + public void close() + throws Exception + { + partitions.close(); + constraints.close(); + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + GetSplitsRequest that = (GetSplitsRequest) o; + + Objects.equal(this.tableName, that.tableName); + Objects.equal(this.partitions, that.partitions); + CollectionsUtils.equals(this.partitionCols, that.partitionCols); + Objects.equal(this.continuationToken, that.continuationToken); + Objects.equal(this.getRequestType(), that.getRequestType()); + Objects.equal(this.getCatalogName(), that.getCatalogName()); + + return Objects.equal(this.tableName, that.tableName) && + Objects.equal(this.partitions, that.partitions) && + CollectionsUtils.equals(this.partitionCols, that.partitionCols) && + Objects.equal(this.continuationToken, that.continuationToken) && + Objects.equal(this.getRequestType(), that.getRequestType()) && + Objects.equal(this.getCatalogName(), that.getCatalogName()); + } + + @Override + public int hashCode() + { + return Objects.hashCode(tableName, partitions, partitionCols, continuationToken, getRequestType(), getCatalogName()); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/GetSplitsResponse.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/GetSplitsResponse.java new file mode 100644 index 0000000000..025ec5cc53 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/GetSplitsResponse.java @@ -0,0 +1,122 @@ +package com.amazonaws.athena.connector.lambda.metadata; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.Objects; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import static java.util.Objects.requireNonNull; + +public class GetSplitsResponse + extends MetadataResponse +{ + private final Set splits; + private final String continuationToken; + + @JsonCreator + public GetSplitsResponse(@JsonProperty("catalogName") String catalogName, + @JsonProperty("splits") Set splits, + @JsonProperty("continuationToken") String continuationToken) + { + super(MetadataRequestType.GET_SPLITS, catalogName); + requireNonNull(splits, "splits is null"); + this.splits = Collections.unmodifiableSet(splits); + this.continuationToken = continuationToken; + } + + public GetSplitsResponse(String catalogName, + Set splits) + { + super(MetadataRequestType.GET_SPLITS, catalogName); + requireNonNull(splits, "splits is null"); + this.splits = Collections.unmodifiableSet(splits); + this.continuationToken = null; + } + + public GetSplitsResponse(String catalogName, + Split split) + { + super(MetadataRequestType.GET_SPLITS, catalogName); + requireNonNull(split, "split is null"); + Set splits = new HashSet<>(); + splits.add(split); + this.splits = Collections.unmodifiableSet(splits); + this.continuationToken = null; + } + + @JsonProperty + public Set getSplits() + { + return splits; + } + + @JsonProperty + public String getContinuationToken() + { + return continuationToken; + } + + @Override + public void close() + throws Exception + { + //NoOp + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + GetSplitsResponse that = (GetSplitsResponse) o; + + return Objects.equal(this.splits, that.splits) && + Objects.equal(this.continuationToken, that.continuationToken) && + Objects.equal(this.getRequestType(), that.getRequestType()) && + Objects.equal(this.getCatalogName(), that.getCatalogName()); + } + + @Override + public int hashCode() + { + return Objects.hashCode(splits, continuationToken, getRequestType(), getCatalogName()); + } + + @Override + public String toString() + { + return "GetSplitsResponse{" + + "splitSize=" + splits.size() + + ", continuationToken='" + continuationToken + '\'' + + '}'; + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/GetTableLayoutRequest.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/GetTableLayoutRequest.java new file mode 100644 index 0000000000..e1c7f67574 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/GetTableLayoutRequest.java @@ -0,0 +1,130 @@ +package com.amazonaws.athena.connector.lambda.metadata; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import com.amazonaws.athena.connector.lambda.security.FederatedIdentity; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.Objects; +import org.apache.arrow.vector.types.pojo.Schema; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import static java.util.Objects.requireNonNull; + +public class GetTableLayoutRequest + extends MetadataRequest +{ + private final TableName tableName; + private final Constraints constraints; + private final Schema schema; + private final Set partitionCols; + + @JsonCreator + public GetTableLayoutRequest(@JsonProperty("identity") FederatedIdentity identity, + @JsonProperty("queryId") String queryId, + @JsonProperty("catalogName") String catalogName, + @JsonProperty("tableName") TableName tableName, + @JsonProperty("constraints") Constraints constraints, + @JsonProperty("schema") Schema schema, + @JsonProperty("partitionCols") Set partitionCols) + { + super(identity, MetadataRequestType.GET_TABLE_LAYOUT, queryId, catalogName); + requireNonNull(partitionCols, "partitionCols is null"); + this.tableName = requireNonNull(tableName, "tableName is null"); + this.constraints = requireNonNull(constraints, "constraints is null"); + this.schema = requireNonNull(schema, "schema is null"); + this.partitionCols = Collections.unmodifiableSet(new HashSet<>(partitionCols)); + } + + public TableName getTableName() + { + return tableName; + } + + public Constraints getConstraints() + { + return constraints; + } + + public Schema getSchema() + { + return schema; + } + + public Set getPartitionCols() + { + return partitionCols; + } + + @Override + public void close() + throws Exception + { + for (ValueSet next : constraints.getSummary().values()) { + next.close(); + } + constraints.close(); + } + + @Override + public String toString() + { + return "GetTableLayoutRequest{" + + "queryId=" + getQueryId() + + ", tableName=" + tableName + + ", constraints=" + constraints + + ", schema=" + schema + + ", partitionCols=" + partitionCols + + '}'; + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + GetTableLayoutRequest that = (GetTableLayoutRequest) o; + + return Objects.equal(this.tableName, that.tableName) && + Objects.equal(this.constraints, that.constraints) && + Objects.equal(this.schema, that.schema) && + Objects.equal(this.partitionCols, that.partitionCols) && + Objects.equal(this.getRequestType(), that.getRequestType()) && + Objects.equal(this.getCatalogName(), that.getCatalogName()); + } + + @Override + public int hashCode() + { + return Objects.hashCode(tableName, constraints, schema, partitionCols, getRequestType(), getCatalogName()); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/GetTableLayoutResponse.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/GetTableLayoutResponse.java new file mode 100644 index 0000000000..6d57d92fed --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/GetTableLayoutResponse.java @@ -0,0 +1,102 @@ +package com.amazonaws.athena.connector.lambda.metadata; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.MoreObjects; +import com.google.common.base.Objects; + +import static java.util.Objects.requireNonNull; + +public class GetTableLayoutResponse + extends MetadataResponse +{ + private final TableName tableName; + private final Block partitions; + + @JsonCreator + public GetTableLayoutResponse(@JsonProperty("catalogName") String catalogName, + @JsonProperty("tableName") TableName tableName, + @JsonProperty("partitions") Block partitions) + { + super(MetadataRequestType.GET_TABLE_LAYOUT, catalogName); + requireNonNull(tableName, "tableName is null"); + requireNonNull(partitions, "partitions is null"); + this.tableName = tableName; + this.partitions = partitions; + } + + @JsonProperty + public TableName getTableName() + { + return tableName; + } + + @JsonProperty + public Block getPartitions() + { + return partitions; + } + + @Override + public String toString() + { + return MoreObjects.toStringHelper(this) + .add("tableName", tableName) + .add("requestType", getRequestType()) + .add("catalogName", getCatalogName()) + .toString(); + } + + @Override + public void close() + throws Exception + { + partitions.close(); + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + GetTableLayoutResponse that = (GetTableLayoutResponse) o; + + return Objects.equal(this.tableName, that.tableName) && + Objects.equal(this.partitions, that.partitions) && + Objects.equal(this.getRequestType(), that.getRequestType()) && + Objects.equal(this.getCatalogName(), that.getCatalogName()); + } + + @Override + public int hashCode() + { + return Objects.hashCode(tableName, partitions, getRequestType(), getCatalogName()); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/GetTableRequest.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/GetTableRequest.java new file mode 100644 index 0000000000..ceb2b0cd3e --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/GetTableRequest.java @@ -0,0 +1,90 @@ +package com.amazonaws.athena.connector.lambda.metadata; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.security.FederatedIdentity; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.Objects; + +import static java.util.Objects.requireNonNull; + +public class GetTableRequest + extends MetadataRequest +{ + private final TableName tableName; + + @JsonCreator + public GetTableRequest(@JsonProperty("identity") FederatedIdentity identity, + @JsonProperty("queryId") String queryId, + @JsonProperty("catalogName") String catalogName, + @JsonProperty("tableName") TableName tableName) + { + super(identity, MetadataRequestType.GET_TABLE, queryId, catalogName); + requireNonNull(tableName, "tableName is null"); + this.tableName = tableName; + } + + public TableName getTableName() + { + return tableName; + } + + @Override + public void close() + throws Exception + { + //No Op + } + + @Override + public String toString() + { + return "GetTableRequest{" + + "queryId=" + getQueryId() + + ", tableName=" + tableName + + '}'; + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + GetTableRequest that = (GetTableRequest) o; + + return Objects.equal(this.tableName, that.tableName) && + Objects.equal(this.getRequestType(), that.getRequestType()) && + Objects.equal(this.getCatalogName(), that.getCatalogName()); + } + + @Override + public int hashCode() + { + return Objects.hashCode(tableName, getRequestType(), getCatalogName()); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/GetTableResponse.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/GetTableResponse.java new file mode 100644 index 0000000000..804c309597 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/GetTableResponse.java @@ -0,0 +1,120 @@ +package com.amazonaws.athena.connector.lambda.metadata; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.MoreObjects; +import com.google.common.base.Objects; +import org.apache.arrow.vector.types.pojo.Schema; + +import java.util.Collections; +import java.util.Set; + +import static java.util.Objects.requireNonNull; + +public class GetTableResponse + extends MetadataResponse +{ + private final TableName tableName; + private final Schema schema; + private final Set partitionColumns; + + @JsonCreator + public GetTableResponse(@JsonProperty("catalogName") String catalogName, + @JsonProperty("tableName") TableName tableName, + @JsonProperty("schema") Schema schema, + @JsonProperty("partitionColumns") Set partitionColumns) + { + super(MetadataRequestType.GET_TABLE, catalogName); + requireNonNull(tableName, "tableName is null"); + requireNonNull(schema, "schema is null"); + requireNonNull(partitionColumns, "partitionColumns is null"); + this.tableName = tableName; + this.schema = schema; + this.partitionColumns = partitionColumns; + } + + public GetTableResponse(String catalogName, TableName tableName, Schema schema) + { + this(catalogName, tableName, schema, Collections.emptySet()); + } + + public TableName getTableName() + { + return tableName; + } + + public Schema getSchema() + { + return schema; + } + + public Set getPartitionColumns() + { + return Collections.unmodifiableSet(partitionColumns); + } + + @Override + public String toString() + { + return MoreObjects.toStringHelper(this) + .add("tableName", tableName) + .add("schema", schema) + .add("partitionColumns", partitionColumns) + .add("requestType", getRequestType()) + .add("catalogName", getCatalogName()) + .toString(); + } + + @Override + public void close() + throws Exception + { + //No Op + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + GetTableResponse that = (GetTableResponse) o; + + return Objects.equal(this.tableName, that.tableName) && + Objects.equal(this.schema, that.schema) && + Objects.equal(this.partitionColumns, that.partitionColumns) && + Objects.equal(this.getRequestType(), that.getRequestType()) && + Objects.equal(this.getCatalogName(), that.getCatalogName()); + } + + @Override + public int hashCode() + { + return Objects.hashCode(tableName, schema, partitionColumns, getRequestType(), getRequestType()); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/ListSchemasRequest.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/ListSchemasRequest.java new file mode 100644 index 0000000000..afaeefb56c --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/ListSchemasRequest.java @@ -0,0 +1,73 @@ +package com.amazonaws.athena.connector.lambda.metadata; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.security.FederatedIdentity; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.Objects; + +public class ListSchemasRequest + extends MetadataRequest +{ + @JsonCreator + public ListSchemasRequest(@JsonProperty("identity") FederatedIdentity identity, + @JsonProperty("queryId") String queryId, + @JsonProperty("catalogName") String catalogName) + { + super(identity, MetadataRequestType.LIST_SCHEMAS, queryId, catalogName); + } + + @Override + public void close() + throws Exception + { + //No Op + } + + @Override + public String toString() + { + return "ListSchemasRequest{" + "queryId=" + getQueryId() + "}"; + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + ListSchemasRequest that = (ListSchemasRequest) o; + + return Objects.equal(this.getRequestType(), that.getRequestType()) && + Objects.equal(this.getCatalogName(), that.getCatalogName()); + } + + @Override + public int hashCode() + { + return Objects.hashCode(getRequestType(), getCatalogName()); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/ListSchemasResponse.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/ListSchemasResponse.java new file mode 100644 index 0000000000..3eb0c7ab82 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/ListSchemasResponse.java @@ -0,0 +1,89 @@ +package com.amazonaws.athena.connector.lambda.metadata; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.CollectionsUtils; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.Objects; + +import java.util.Collection; +import java.util.Collections; + +import static java.util.Objects.requireNonNull; + +public class ListSchemasResponse + extends MetadataResponse +{ + private final Collection schemas; + + @JsonCreator + public ListSchemasResponse(@JsonProperty("catalogName") String catalogName, + @JsonProperty("schemas") Collection schemas) + { + super(MetadataRequestType.LIST_SCHEMAS, catalogName); + requireNonNull(schemas, "schemas is null"); + this.schemas = Collections.unmodifiableCollection(schemas); + } + + public Collection getSchemas() + { + return schemas; + } + + @Override + public void close() + throws Exception + { + //No Op + } + + @Override + public String toString() + { + return "ListSchemasResponse{" + + "schemas=" + schemas + + '}'; + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + ListSchemasResponse that = (ListSchemasResponse) o; + + return CollectionsUtils.equals(this.schemas, that.schemas) && + Objects.equal(this.getRequestType(), that.getRequestType()) && + Objects.equal(this.getCatalogName(), that.getCatalogName()); + } + + @Override + public int hashCode() + { + return Objects.hashCode(schemas, getRequestType(), getCatalogName()); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/ListTablesRequest.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/ListTablesRequest.java new file mode 100644 index 0000000000..85cc84c8e9 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/ListTablesRequest.java @@ -0,0 +1,90 @@ +package com.amazonaws.athena.connector.lambda.metadata; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.security.FederatedIdentity; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.Objects; + +public class ListTablesRequest + extends MetadataRequest +{ + private final String schemaName; + + /** + * @param catalogName The name of the catalog being requested. + * @param schemaName This may be null if no specific schema is requested. + */ + @JsonCreator + public ListTablesRequest(@JsonProperty("identity") FederatedIdentity identity, + @JsonProperty("queryId") String queryId, + @JsonProperty("catalogName") String catalogName, + @JsonProperty("schemaName") String schemaName) + { + super(identity, MetadataRequestType.LIST_TABLES, queryId, catalogName); + this.schemaName = schemaName; + } + + public String getSchemaName() + { + return schemaName; + } + + @Override + public void close() + throws Exception + { + //No Op + } + + @Override + public String toString() + { + return "ListTablesRequest{" + + "queryId=" + getQueryId() + + ", schemaName='" + schemaName + '\'' + + '}'; + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + ListTablesRequest that = (ListTablesRequest) o; + + return Objects.equal(this.schemaName, that.schemaName) && + Objects.equal(this.getRequestType(), that.getRequestType()) && + Objects.equal(this.getCatalogName(), that.getCatalogName()); + } + + @Override + public int hashCode() + { + return Objects.hashCode(schemaName, getRequestType(), getCatalogName()); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/ListTablesResponse.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/ListTablesResponse.java new file mode 100644 index 0000000000..ab72a67ef0 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/ListTablesResponse.java @@ -0,0 +1,90 @@ +package com.amazonaws.athena.connector.lambda.metadata; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.CollectionsUtils; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.Objects; + +import java.util.Collection; +import java.util.Collections; + +import static java.util.Objects.requireNonNull; + +public class ListTablesResponse + extends MetadataResponse +{ + private final Collection tables; + + @JsonCreator + public ListTablesResponse(@JsonProperty("catalogName") String catalogName, + @JsonProperty("tables") Collection tables) + { + super(MetadataRequestType.LIST_TABLES, catalogName); + requireNonNull(tables, "tables is null"); + this.tables = Collections.unmodifiableCollection(tables); + } + + public Collection getTables() + { + return tables; + } + + @Override + public void close() + throws Exception + { + //No Op + } + + @Override + public String toString() + { + return "ListTablesResponse{" + + "tables=" + tables + + '}'; + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + ListTablesResponse that = (ListTablesResponse) o; + + return CollectionsUtils.equals(this.tables, that.tables) && + Objects.equal(this.getRequestType(), that.getRequestType()) && + Objects.equal(this.getCatalogName(), that.getCatalogName()); + } + + @Override + public int hashCode() + { + return Objects.hashCode(tables, getRequestType(), getCatalogName()); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/MetadataRequest.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/MetadataRequest.java new file mode 100644 index 0000000000..5533f72b9f --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/MetadataRequest.java @@ -0,0 +1,59 @@ +package com.amazonaws.athena.connector.lambda.metadata; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.request.FederationRequest; +import com.amazonaws.athena.connector.lambda.security.FederatedIdentity; + +import static java.util.Objects.requireNonNull; + +public abstract class MetadataRequest + extends FederationRequest +{ + private final MetadataRequestType requestType; + private final String queryId; + private final String catalogName; + + public MetadataRequest(FederatedIdentity identity, MetadataRequestType requestType, String queryId, String catalogName) + { + super(identity); + requireNonNull(requestType, "requestType is null"); + requireNonNull(catalogName, "catalogName is null"); + this.requestType = requestType; + this.catalogName = catalogName; + this.queryId = queryId; + } + + public MetadataRequestType getRequestType() + { + return requestType; + } + + public String getCatalogName() + { + return catalogName; + } + + public String getQueryId() + { + return queryId; + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/MetadataRequestType.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/MetadataRequestType.java new file mode 100644 index 0000000000..ce76ebf7cb --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/MetadataRequestType.java @@ -0,0 +1,30 @@ +package com.amazonaws.athena.connector.lambda.metadata; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +public enum MetadataRequestType +{ + LIST_TABLES, + LIST_SCHEMAS, + GET_TABLE, + GET_TABLE_LAYOUT, + GET_SPLITS; +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/MetadataResponse.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/MetadataResponse.java new file mode 100644 index 0000000000..ba768650c1 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/MetadataResponse.java @@ -0,0 +1,50 @@ +package com.amazonaws.athena.connector.lambda.metadata; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.request.FederationResponse; + +import static java.util.Objects.requireNonNull; + +public abstract class MetadataResponse + extends FederationResponse +{ + private final MetadataRequestType requestType; + private final String catalogName; + + public MetadataResponse(MetadataRequestType requestType, String catalogName) + { + requireNonNull(requestType, "requestType is null"); + requireNonNull(catalogName, "catalogName is null"); + this.requestType = requestType; + this.catalogName = catalogName; + } + + public MetadataRequestType getRequestType() + { + return requestType; + } + + public String getCatalogName() + { + return catalogName; + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/MetadataService.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/MetadataService.java new file mode 100644 index 0000000000..120a467c36 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/MetadataService.java @@ -0,0 +1,29 @@ +package com.amazonaws.athena.connector.lambda.metadata; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.services.lambda.invoke.LambdaFunction; + +public interface MetadataService +{ + @LambdaFunction(functionName = "metadata") + MetadataResponse getMetadata(final MetadataRequest request); +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/glue/DefaultGlueType.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/glue/DefaultGlueType.java new file mode 100644 index 0000000000..8c203614e3 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/glue/DefaultGlueType.java @@ -0,0 +1,82 @@ +package com.amazonaws.athena.connector.lambda.metadata.glue; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.ArrowType; + +import java.util.HashMap; +import java.util.Map; + +public enum DefaultGlueType +{ + INT("int", Types.MinorType.INT.getType()), + VARCHAR("string", Types.MinorType.VARCHAR.getType()), + BIGINT("bigint", Types.MinorType.BIGINT.getType()), + DOUBLE("double", Types.MinorType.FLOAT8.getType()), + FLOAT("float", Types.MinorType.FLOAT4.getType()), + SMALLINT("smallint", Types.MinorType.SMALLINT.getType()), + TINYINT("tinyint", Types.MinorType.TINYINT.getType()), + BIT("boolean", Types.MinorType.BIT.getType()), + VARBINARY("binary", Types.MinorType.VARBINARY.getType()); + + private static final Map TYPE_MAP = new HashMap<>(); + + static { + for (DefaultGlueType next : DefaultGlueType.values()) { + TYPE_MAP.put(next.id, next); + } + } + + private String id; + private ArrowType arrowType; + + DefaultGlueType(String id, ArrowType arrowType) + { + this.id = id; + this.arrowType = arrowType; + } + + public static DefaultGlueType fromId(String id) + { + DefaultGlueType result = TYPE_MAP.get(id.toLowerCase()); + if (result == null) { + throw new IllegalArgumentException("Unknown DefaultGlueType for id: " + id); + } + + return result; + } + + public static ArrowType toArrowType(String id) + { + DefaultGlueType result = TYPE_MAP.get(id.toLowerCase()); + if (result == null) { + return null; + } + + return result.getArrowType(); + } + + public ArrowType getArrowType() + { + return arrowType; + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/glue/GlueFieldLexer.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/glue/GlueFieldLexer.java new file mode 100644 index 0000000000..6c11b02fc8 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/glue/GlueFieldLexer.java @@ -0,0 +1,125 @@ +package com.amazonaws.athena.connector.lambda.metadata.glue; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.FieldBuilder; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.arrow.vector.types.pojo.Field; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class GlueFieldLexer +{ + private static final Logger logger = LoggerFactory.getLogger(GlueFieldLexer.class); + + private static final String STRUCT = "struct"; + private static final String LIST = "array"; + + private static final BaseTypeMapper DEFAULT_TYPE_MAPPER = (String type) -> DefaultGlueType.toArrowType(type); + + private GlueFieldLexer() {} + + public interface BaseTypeMapper + { + //Return Null if the supplied value is not a base type + ArrowType getType(String type); + } + + public static Field lex(String name, String input) + { + if (DEFAULT_TYPE_MAPPER.getType(input) != null) { + return FieldBuilder.newBuilder(name, DEFAULT_TYPE_MAPPER.getType(input)).build(); + } + + GlueTypeParser parser = new GlueTypeParser(input); + return lexComplex(name, parser.next(), parser, DEFAULT_TYPE_MAPPER); + } + + public static Field lex(String name, String input, BaseTypeMapper mapper) + { + if (mapper.getType(input) != null) { + return FieldBuilder.newBuilder(name, mapper.getType(input)).build(); + } + + GlueTypeParser parser = new GlueTypeParser(input); + return lexComplex(name, parser.next(), parser, mapper); + } + + private static Field lexComplex(String name, GlueTypeParser.Token startToken, GlueTypeParser parser, BaseTypeMapper mapper) + { + FieldBuilder fieldBuilder; + + logger.debug("lexComplex: enter - {}", name); + if (startToken.getMarker() != GlueTypeParser.FIELD_START) { + throw new RuntimeException("Parse error, expected " + GlueTypeParser.FIELD_START + + " but found " + startToken.getMarker()); + } + + if (startToken.getValue().toLowerCase().equals(STRUCT)) { + fieldBuilder = FieldBuilder.newBuilder(name, Types.MinorType.STRUCT.getType()); + } + else if (startToken.getValue().toLowerCase().equals(LIST)) { + GlueTypeParser.Token arrayType = parser.next(); + return FieldBuilder.newBuilder(name, Types.MinorType.LIST.getType()) + .addField(FieldBuilder.newBuilder(name, mapper.getType(arrayType.getValue())).build()) + .build(); + } + else { + throw new RuntimeException("Unexpected start type " + startToken.getValue()); + } + + while (parser.hasNext() && parser.currentToken().getMarker() != GlueTypeParser.FIELD_END) { + Field child = lex(parser.next(), parser, mapper); + fieldBuilder.addField(child); + } + parser.next(); + + logger.debug("lexComplex: exit - {}", name); + return fieldBuilder.build(); + } + + private static Field lex(GlueTypeParser.Token startToken, GlueTypeParser parser, BaseTypeMapper mapper) + { + GlueTypeParser.Token nameToken = startToken; + logger.debug("lex: enter - {}", nameToken.getValue()); + if (!nameToken.getMarker().equals(GlueTypeParser.FIELD_DIV)) { + throw new RuntimeException("Expected Field DIV but found " + nameToken.getMarker() + + " while processing " + nameToken.getValue()); + } + + String name = nameToken.getValue(); + + GlueTypeParser.Token typeToken = parser.next(); + if (typeToken.getMarker().equals(GlueTypeParser.FIELD_START)) { + logger.debug("lex: exit - {}", nameToken.getValue()); + return lexComplex(name, typeToken, parser, mapper); + } + else if (typeToken.getMarker().equals(GlueTypeParser.FIELD_SEP) || + typeToken.getMarker().equals(GlueTypeParser.FIELD_END) + ) { + logger.debug("lex: exit - {}", nameToken.getValue()); + return FieldBuilder.newBuilder(name, mapper.getType(typeToken.getValue())).build(); + } + throw new RuntimeException("Unexpected Token " + typeToken.getValue() + "[" + typeToken.getMarker() + "]" + + " @ " + typeToken.getPos() + " while processing " + name); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/glue/GlueTypeParser.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/glue/GlueTypeParser.java new file mode 100644 index 0000000000..97ea59dfdb --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/metadata/glue/GlueTypeParser.java @@ -0,0 +1,153 @@ +package com.amazonaws.athena.connector.lambda.metadata.glue; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +public class GlueTypeParser +{ + private static final Logger logger = LoggerFactory.getLogger(GlueTypeParser.class); + + protected static final Character FIELD_START = '<'; + protected static final Character FIELD_END = '>'; + protected static final Character FIELD_DIV = ':'; + protected static final Character FIELD_SEP = ','; + private static final Set TOKENS = new HashSet<>(); + + static { + TOKENS.add(FIELD_START); + TOKENS.add(FIELD_END); + TOKENS.add(FIELD_DIV); + TOKENS.add(FIELD_SEP); + } + + private final String input; + private int pos; + private Token current = null; + + public GlueTypeParser(String input) + { + this.input = input; + this.pos = 0; + } + + public boolean hasNext() + { + return pos < input.length(); + } + + public Token next() + { + StringBuilder sb = new StringBuilder(); + int readPos = pos; + while (input.length() > readPos) { + Character last = input.charAt(readPos++); + if (last.equals(' ')) { + //NoOp + } + else if (!TOKENS.contains(last)) { + sb.append(last); + } + else { + pos = readPos; + current = new Token(sb.toString(), last, readPos); + logger.debug("next: {}", current); + return current; + } + } + pos = readPos; + + current = new Token(sb.toString(), null, readPos); + logger.debug("next: {}", current); + + return current; + } + + public Token currentToken() + { + return current; + } + + public static class Token + { + private final String value; + private final Character marker; + private final int pos; + + public Token(String value, Character marker, int pos) + { + this.value = value; + this.marker = marker; + this.pos = pos; + } + + public String getValue() + { + return value; + } + + public Character getMarker() + { + return marker; + } + + public int getPos() + { + return pos; + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Token token = (Token) o; + return getPos() == token.getPos() && + getValue().equals(token.getValue()) && + getMarker().equals(token.getMarker()); + } + + @Override + public int hashCode() + { + return Objects.hash(getValue(), getMarker(), getPos()); + } + + @Override + public String toString() + { + return "Token{" + + "value='" + value + '\'' + + ", marker=" + marker + + ", pos=" + pos + + '}'; + } + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/records/ReadRecordsRequest.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/records/ReadRecordsRequest.java new file mode 100644 index 0000000000..3b32f801c3 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/records/ReadRecordsRequest.java @@ -0,0 +1,155 @@ +package com.amazonaws.athena.connector.lambda.records; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.security.FederatedIdentity; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.MoreObjects; +import com.google.common.base.Objects; +import org.apache.arrow.vector.types.pojo.Schema; + +import static java.util.Objects.requireNonNull; + +public class ReadRecordsRequest + extends RecordRequest +{ + private final TableName tableName; + private final Schema schema; + private final Split split; + private final Constraints constraints; + private final long maxBlockSize; + private final long maxInlineBlockSize; + + public ReadRecordsRequest(@JsonProperty("identity") FederatedIdentity identity, + @JsonProperty("catalogName") String catalogName, + @JsonProperty("queryId") String queryId, + @JsonProperty("tableName") TableName tableName, + @JsonProperty("schema") Schema schema, + @JsonProperty("split") Split split, + @JsonProperty("constraints") Constraints constraints, + @JsonProperty("maxBlockSize") long maxBlockSize, + @JsonProperty("maxInlineBlockSize") long maxInlineBlockSize) + { + super(identity, RecordRequestType.READ_RECORDS, catalogName, queryId); + requireNonNull(schema, "schema is null"); + requireNonNull(tableName, "tableName is null"); + requireNonNull(split, "split is null"); + requireNonNull(constraints, "constraints is null"); + this.schema = schema; + this.tableName = tableName; + this.split = split; + this.maxBlockSize = maxBlockSize; + this.maxInlineBlockSize = maxInlineBlockSize; + this.constraints = constraints; + } + + @JsonProperty + public TableName getTableName() + { + return tableName; + } + + @JsonProperty + public Schema getSchema() + { + return schema; + } + + @JsonProperty + public Split getSplit() + { + return split; + } + + @JsonProperty + public long getMaxInlineBlockSize() + { + return maxInlineBlockSize; + } + + @JsonProperty + public long getMaxBlockSize() + { + return maxBlockSize; + } + + @JsonProperty + public Constraints getConstraints() + { + return constraints; + } + + @Override + public void close() + throws Exception + { + constraints.close(); + } + + @Override + public String toString() + { + return MoreObjects.toStringHelper(this) + .add("queryId", getQueryId()) + .add("tableName", tableName) + .add("schema", schema) + .add("split", split) + .add("requestType", getRequestType()) + .add("catalogName", getCatalogName()) + .add("maxBlockSize", maxBlockSize) + .add("maxInlineBlockSize", maxInlineBlockSize) + .add("constraints", constraints) + .toString(); + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + ReadRecordsRequest that = (ReadRecordsRequest) o; + + return Objects.equal(this.tableName, that.tableName) && + Objects.equal(this.schema, that.schema) && + Objects.equal(this.split, that.split) && + Objects.equal(this.constraints, that.constraints) && + Objects.equal(this.maxBlockSize, that.maxBlockSize) && + Objects.equal(this.maxInlineBlockSize, that.maxInlineBlockSize) && + Objects.equal(this.getRequestType(), that.getRequestType()) && + Objects.equal(this.getCatalogName(), that.getCatalogName()) && + Objects.equal(this.getQueryId(), that.getQueryId()); + } + + @Override + public int hashCode() + { + return Objects.hashCode(tableName, schema, split, constraints, maxBlockSize, maxInlineBlockSize, + getRequestType(), getCatalogName(), getQueryId()); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/records/ReadRecordsResponse.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/records/ReadRecordsResponse.java new file mode 100644 index 0000000000..059ef87baf --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/records/ReadRecordsResponse.java @@ -0,0 +1,105 @@ +package com.amazonaws.athena.connector.lambda.records; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.MoreObjects; +import com.google.common.base.Objects; +import org.apache.arrow.vector.types.pojo.Schema; + +import java.beans.Transient; + +import static java.util.Objects.requireNonNull; + +public class ReadRecordsResponse + extends RecordResponse +{ + private final Block records; + + @JsonCreator + public ReadRecordsResponse(@JsonProperty("catalogName") String catalogName, + @JsonProperty("records") Block records) + { + super(RecordRequestType.READ_RECORDS, catalogName); + requireNonNull(records, "records is null"); + this.records = records; + } + + @Transient + public Schema getSchema() + { + return records.getSchema(); + } + + @JsonProperty + public Block getRecords() + { + return records; + } + + @Transient + public int getRecordCount() + { + return records.getRowCount(); + } + + @Override + public void close() + throws Exception + { + records.close(); + } + + @Override + public String toString() + { + return MoreObjects.toStringHelper(this) + .add("records", records) + .add("requestType", getRequestType()) + .add("catalogName", getCatalogName()) + .toString(); + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + ReadRecordsResponse that = (ReadRecordsResponse) o; + + return Objects.equal(this.records, that.records) && + Objects.equal(this.getRequestType(), that.getRequestType()) && + Objects.equal(this.getCatalogName(), that.getCatalogName()); + } + + @Override + public int hashCode() + { + return Objects.hashCode(records, getRequestType(), getCatalogName()); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/records/RecordRequest.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/records/RecordRequest.java new file mode 100644 index 0000000000..c3fb204b08 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/records/RecordRequest.java @@ -0,0 +1,60 @@ +package com.amazonaws.athena.connector.lambda.records; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.request.FederationRequest; +import com.amazonaws.athena.connector.lambda.security.FederatedIdentity; + +import static java.util.Objects.requireNonNull; + +public abstract class RecordRequest + extends FederationRequest +{ + private final RecordRequestType requestType; + private final String catalogName; + private final String queryId; + + public RecordRequest(FederatedIdentity identity, RecordRequestType requestType, String catalogName, String queryId) + { + super(identity); + requireNonNull(requestType, "requestType is null"); + requireNonNull(catalogName, "catalogName is null"); + requireNonNull(queryId, "queryId is null"); + this.requestType = requestType; + this.catalogName = catalogName; + this.queryId = queryId; + } + + public RecordRequestType getRequestType() + { + return requestType; + } + + public String getCatalogName() + { + return catalogName; + } + + public String getQueryId() + { + return queryId; + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/records/RecordRequestType.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/records/RecordRequestType.java new file mode 100644 index 0000000000..7c6e91fe6e --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/records/RecordRequestType.java @@ -0,0 +1,26 @@ +package com.amazonaws.athena.connector.lambda.records; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +public enum RecordRequestType +{ + READ_RECORDS; +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/records/RecordResponse.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/records/RecordResponse.java new file mode 100644 index 0000000000..9ced8bd182 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/records/RecordResponse.java @@ -0,0 +1,50 @@ +package com.amazonaws.athena.connector.lambda.records; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.request.FederationResponse; + +import static java.util.Objects.requireNonNull; + +public abstract class RecordResponse + extends FederationResponse +{ + private final RecordRequestType requestType; + private final String catalogName; + + public RecordResponse(RecordRequestType requestType, String catalogName) + { + requireNonNull(requestType, "requestType is null"); + requireNonNull(catalogName, "catalogName is null"); + this.requestType = requestType; + this.catalogName = catalogName; + } + + public RecordRequestType getRequestType() + { + return requestType; + } + + public String getCatalogName() + { + return catalogName; + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/records/RecordService.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/records/RecordService.java new file mode 100644 index 0000000000..6ed540ae7e --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/records/RecordService.java @@ -0,0 +1,29 @@ +package com.amazonaws.athena.connector.lambda.records; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.services.lambda.invoke.LambdaFunction; + +public interface RecordService +{ + @LambdaFunction(functionName = "record") + RecordResponse readRecords(final RecordRequest request); +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/records/RemoteReadRecordsResponse.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/records/RemoteReadRecordsResponse.java new file mode 100644 index 0000000000..f57b252f83 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/records/RemoteReadRecordsResponse.java @@ -0,0 +1,125 @@ +package com.amazonaws.athena.connector.lambda.records; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.domain.spill.SpillLocation; +import com.amazonaws.athena.connector.lambda.security.EncryptionKey; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.MoreObjects; +import com.google.common.base.Objects; +import org.apache.arrow.vector.types.pojo.Schema; + +import java.beans.Transient; +import java.util.Collections; +import java.util.List; + +import static java.util.Objects.requireNonNull; + +public class RemoteReadRecordsResponse + extends RecordResponse +{ + private final Schema schema; + private final List remoteBlocks; + private final EncryptionKey encryptionKey; + + @JsonCreator + public RemoteReadRecordsResponse(@JsonProperty("catalogName") String catalogName, + @JsonProperty("schema") Schema schema, + @JsonProperty("remoteBlocks") List remoteBlocks, + @JsonProperty("encryptionKey") EncryptionKey encryptionKey) + { + super(RecordRequestType.READ_RECORDS, catalogName); + requireNonNull(schema, "schema is null"); + requireNonNull(remoteBlocks, "remoteBlocks is null"); + this.schema = schema; + this.remoteBlocks = Collections.unmodifiableList(remoteBlocks); + this.encryptionKey = encryptionKey; + } + + @JsonProperty + public Schema getSchema() + { + return schema; + } + + @JsonProperty + public List getRemoteBlocks() + { + return remoteBlocks; + } + + @Transient + public int getNumberBlocks() + { + return remoteBlocks.size(); + } + + @JsonProperty + public EncryptionKey getEncryptionKey() + { + return encryptionKey; + } + + @Override + public void close() + throws Exception + { + //NoOp + } + + @Override + public String toString() + { + return MoreObjects.toStringHelper(this) + .add("schema", schema) + .add("remoteBlocks", remoteBlocks) + .add("encryptionKey", "XXXXXX") + .add("requestType", getRequestType()) + .add("catalogName", getCatalogName()) + .toString(); + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + RemoteReadRecordsResponse that = (RemoteReadRecordsResponse) o; + + return Objects.equal(this.schema, that.schema) && + Objects.equal(this.remoteBlocks, that.remoteBlocks) && + Objects.equal(this.encryptionKey, that.encryptionKey) && + Objects.equal(this.getRequestType(), that.getRequestType()) && + Objects.equal(this.getCatalogName(), that.getCatalogName()); + } + + @Override + public int hashCode() + { + return Objects.hashCode(schema, remoteBlocks, encryptionKey, getRequestType(), getCatalogName()); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/request/FederationRequest.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/request/FederationRequest.java new file mode 100644 index 0000000000..4a4df3e1cc --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/request/FederationRequest.java @@ -0,0 +1,66 @@ +package com.amazonaws.athena.connector.lambda.request; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesRequest; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.athena.connector.lambda.security.FederatedIdentity; +import com.amazonaws.athena.connector.lambda.udf.UserDefinedFunctionRequest; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY) +@JsonSubTypes({ + @JsonSubTypes.Type(value = ListSchemasRequest.class, name = "ListSchemasRequest"), + @JsonSubTypes.Type(value = ListTablesRequest.class, name = "ListTablesRequest"), + @JsonSubTypes.Type(value = GetTableRequest.class, name = "GetTableRequest"), + @JsonSubTypes.Type(value = GetTableLayoutRequest.class, name = "GetTableLayoutRequest"), + @JsonSubTypes.Type(value = GetSplitsRequest.class, name = "GetSplitsRequest"), + @JsonSubTypes.Type(value = ReadRecordsRequest.class, name = "ReadRecordsRequest"), + @JsonSubTypes.Type(value = UserDefinedFunctionRequest.class, name = "UserDefinedFunctionRequest"), + @JsonSubTypes.Type(value = PingRequest.class, name = "PingRequest") +}) +public abstract class FederationRequest + implements AutoCloseable +{ + private final FederatedIdentity identity; + + public FederationRequest() + { + identity = null; + } + + public FederationRequest(FederatedIdentity identity) + { + this.identity = identity; + } + + public FederatedIdentity getIdentity() + { + return identity; + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/request/FederationResponse.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/request/FederationResponse.java new file mode 100644 index 0000000000..f0cb86cc5b --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/request/FederationResponse.java @@ -0,0 +1,50 @@ +package com.amazonaws.athena.connector.lambda.request; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesResponse; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsResponse; +import com.amazonaws.athena.connector.lambda.records.RemoteReadRecordsResponse; +import com.amazonaws.athena.connector.lambda.udf.UserDefinedFunctionResponse; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY) +@JsonSubTypes({ + @JsonSubTypes.Type(value = ListSchemasResponse.class, name = "ListSchemasResponse"), + @JsonSubTypes.Type(value = ListTablesResponse.class, name = "ListTablesResponse"), + @JsonSubTypes.Type(value = GetTableResponse.class, name = "GetTableResponse"), + @JsonSubTypes.Type(value = GetTableLayoutResponse.class, name = "GetTableLayoutResponse"), + @JsonSubTypes.Type(value = GetSplitsResponse.class, name = "GetSplitsResponse"), + @JsonSubTypes.Type(value = ReadRecordsResponse.class, name = "ReadRecordsResponse"), + @JsonSubTypes.Type(value = RemoteReadRecordsResponse.class, name = "RemoteReadRecordsResponse"), + @JsonSubTypes.Type(value = UserDefinedFunctionResponse.class, name = "UserDefinedFunctionResponse"), + @JsonSubTypes.Type(value = PingResponse.class, name = "PingResponse") +}) +public abstract class FederationResponse implements AutoCloseable +{ +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/request/PingRequest.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/request/PingRequest.java new file mode 100644 index 0000000000..18bcbc4972 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/request/PingRequest.java @@ -0,0 +1,74 @@ +package com.amazonaws.athena.connector.lambda.request; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.security.FederatedIdentity; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import static java.util.Objects.requireNonNull; + +public class PingRequest + extends FederationRequest +{ + private final String catalogName; + private final String queryId; + + @JsonCreator + public PingRequest(@JsonProperty("identity") FederatedIdentity identity, + @JsonProperty("catalogName") String catalogName, + @JsonProperty("queryId") String queryId) + { + super(identity); + requireNonNull(catalogName, "catalogName is null"); + requireNonNull(queryId, "queryId is null"); + this.catalogName = catalogName; + this.queryId = queryId; + } + + @JsonProperty("catalogName") + public String getCatalogName() + { + return catalogName; + } + + @JsonProperty("queryId") + public String getQueryId() + { + return queryId; + } + + @Override + public void close() + throws Exception + { + //no-op + } + + @Override + public String toString() + { + return "PingRequest{" + + "catalogName='" + catalogName + '\'' + + ", queryId='" + queryId + '\'' + + '}'; + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/request/PingResponse.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/request/PingResponse.java new file mode 100644 index 0000000000..d52d4901cb --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/request/PingResponse.java @@ -0,0 +1,91 @@ +package com.amazonaws.athena.connector.lambda.request; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import static java.util.Objects.requireNonNull; + +public class PingResponse + extends FederationResponse +{ + private final int capabilities; + private final String catalogName; + private final String queryId; + private final String sourceType; + + @JsonCreator + public PingResponse(@JsonProperty("catalogName") String catalogName, + @JsonProperty("queryId") String queryId, + @JsonProperty("sourceType") String sourceType, + @JsonProperty("capabilities") int capabilities) + { + requireNonNull(catalogName, "catalogName is null"); + requireNonNull(queryId, "queryId is null"); + this.catalogName = catalogName; + this.queryId = queryId; + this.sourceType = sourceType; + this.capabilities = capabilities; + } + + @JsonProperty("catalogName") + public String getCatalogName() + { + return catalogName; + } + + @JsonProperty("queryId") + public String getQueryId() + { + return queryId; + } + + @JsonProperty("sourceType") + public String getSourceType() + { + return sourceType; + } + + @JsonProperty("capabilities") + public int getCapabilities() + { + return capabilities; + } + + @Override + public void close() + throws Exception + { + //no-op + } + + @Override + public String toString() + { + return "PingRequest{" + + "catalogName='" + catalogName + '\'' + + ", queryId='" + queryId + '\'' + + ", sourceType='" + sourceType + '\'' + + ", capabilities='" + capabilities + '\'' + + '}'; + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/security/AesGcmBlockCrypto.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/security/AesGcmBlockCrypto.java new file mode 100644 index 0000000000..a0912dc5ae --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/security/AesGcmBlockCrypto.java @@ -0,0 +1,131 @@ +package com.amazonaws.athena.connector.lambda.security; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.RecordBatchSerDe; +import org.apache.arrow.vector.types.pojo.Schema; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Security; + +public class AesGcmBlockCrypto + implements BlockCrypto +{ + protected static final int GCM_TAG_LENGTH_BITS = 16 * 8; + protected static final int NONCE_BYTES = 12; + protected static final int KEY_BYTES = 16; + protected static final String KEYSPEC = "AES"; + protected static final String ALGO = "AES/GCM/NoPadding"; + protected static final String ALGO_BC = "BC"; + + private final RecordBatchSerDe serDe; + private final BlockAllocator allocator; + + static { + Security.addProvider(new BouncyCastleProvider()); + } + + public AesGcmBlockCrypto(BlockAllocator allocator) + { + this.serDe = new RecordBatchSerDe(allocator); + this.allocator = allocator; + } + + public byte[] encrypt(EncryptionKey key, Block block) + { + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serDe.serialize(block.getRecordBatch(), out); + + Cipher cipher = makeCipher(Cipher.ENCRYPT_MODE, key); + return cipher.doFinal(out.toByteArray()); + } + catch (BadPaddingException | IllegalBlockSizeException | IOException ex) { + throw new RuntimeException(ex); + } + } + + public Block decrypt(EncryptionKey key, byte[] bytes, Schema schema) + { + try { + Cipher cipher = makeCipher(Cipher.DECRYPT_MODE, key); + byte[] clear = cipher.doFinal(bytes); + + Block resultBlock = allocator.createBlock(schema); + resultBlock.loadRecordBatch(serDe.deserialize(clear)); + + return resultBlock; + } + catch (BadPaddingException | IllegalBlockSizeException | IOException ex) { + throw new RuntimeException(ex); + } + } + + public byte[] decrypt(EncryptionKey key, byte[] bytes) + { + try { + Cipher cipher = makeCipher(Cipher.DECRYPT_MODE, key); + return cipher.doFinal(bytes); + } + catch (BadPaddingException | IllegalBlockSizeException ex) { + throw new RuntimeException(ex); + } + } + + private Cipher makeCipher(int mode, EncryptionKey key) + { + if (key.getNonce().length != NONCE_BYTES) { + throw new RuntimeException("Expected " + NONCE_BYTES + " nonce bytes but found " + key.getNonce().length); + } + + if (key.getKey().length != KEY_BYTES) { + throw new RuntimeException("Expected " + KEY_BYTES + " key bytes but found " + key.getKey().length); + } + + GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH_BITS, key.getNonce()); + SecretKeySpec secretKeySpec = new SecretKeySpec(key.getKey(), KEYSPEC); + + try { + Cipher cipher = Cipher.getInstance(ALGO, ALGO_BC); + cipher.init(mode, secretKeySpec, spec); + return cipher; + } + catch (NoSuchAlgorithmException | InvalidKeyException | InvalidAlgorithmParameterException + | NoSuchProviderException | NoSuchPaddingException ex) { + throw new RuntimeException(ex); + } + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/security/BlockCrypto.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/security/BlockCrypto.java new file mode 100644 index 0000000000..73bd56d1a3 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/security/BlockCrypto.java @@ -0,0 +1,33 @@ +package com.amazonaws.athena.connector.lambda.security; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.Block; +import org.apache.arrow.vector.types.pojo.Schema; + +public interface BlockCrypto +{ + byte[] encrypt(EncryptionKey key, Block block); + + Block decrypt(EncryptionKey key, byte[] bytes, Schema schema); + + byte[] decrypt(EncryptionKey key, byte[] bytes); +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/security/CachableSecretsManager.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/security/CachableSecretsManager.java new file mode 100644 index 0000000000..2340e05f41 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/security/CachableSecretsManager.java @@ -0,0 +1,152 @@ +package com.amazonaws.athena.connector.lambda.security; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.amazonaws.services.secretsmanager.model.GetSecretValueRequest; +import com.amazonaws.services.secretsmanager.model.GetSecretValueResult; +import org.apache.arrow.util.VisibleForTesting; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class CachableSecretsManager +{ + private static final Logger logger = LoggerFactory.getLogger(CachableSecretsManager.class); + + private static final long MAX_CACHE_AGE_MS = 60_000; + protected static final int MAX_CACHE_SIZE = 10; + + private static final String SECRET_PATTERN = "(\\$\\{[a-zA-Z0-9-_\\-]+\\})"; + private static final String SECRET_NAME_PATTERN = "\\$\\{([a-zA-Z0-9-_\\-]+)\\}"; + private static final Pattern PATTERN = Pattern.compile(SECRET_PATTERN); + private static final Pattern NAME_PATTERN = Pattern.compile(SECRET_NAME_PATTERN); + + private final LinkedHashMap cache = new LinkedHashMap<>(); + private final AWSSecretsManager secretsManager; + + public CachableSecretsManager(AWSSecretsManager secretsManager) + { + this.secretsManager = secretsManager; + } + + /** + * Resolves any secrets found in the supplied string, for example: MyString${WithSecret} would have ${WithSecret} + * by the corresponding value of the secret in AWS Secrets Manager with that name. If no such secret is found + * the function throws. + */ + public String resolveSecrets(String rawString) + { + if (rawString == null) { + return rawString; + } + + Matcher m = PATTERN.matcher(rawString); + String result = rawString; + while (m.find()) { + String nextSecret = m.group(1); + Matcher m1 = NAME_PATTERN.matcher(nextSecret); + m1.find(); + result = result.replace(nextSecret, getSecret(m1.group(1))); + } + return result; + } + + public String getSecret(String secretName) + { + CacheEntry cacheEntry = cache.get(secretName); + + if (cacheEntry == null || cacheEntry.getAge() > MAX_CACHE_AGE_MS) { + logger.info("getSecret: Resolving secret[{}].", secretName); + GetSecretValueResult secretValueResult = secretsManager.getSecretValue(new GetSecretValueRequest() + .withSecretId(secretName)); + cacheEntry = new CacheEntry(secretName, secretValueResult.getSecretString()); + evictCache(cache.size() >= MAX_CACHE_SIZE); + cache.put(secretName, cacheEntry); + } + + return cacheEntry.getValue(); + } + + private void evictCache(boolean force) + { + Iterator> itr = cache.entrySet().iterator(); + int removed = 0; + while (itr.hasNext()) { + CacheEntry entry = itr.next().getValue(); + if (entry.getAge() > MAX_CACHE_AGE_MS) { + itr.remove(); + removed++; + } + } + + if (removed == 0 && force) { + //Remove the oldest since we found no expired entries + itr = cache.entrySet().iterator(); + if (itr.hasNext()) { + itr.next(); + itr.remove(); + } + } + } + + @VisibleForTesting + protected void addCacheEntry(String name, String value, long createTime) + { + cache.put(name, new CacheEntry(name, value, createTime)); + } + + private class CacheEntry + { + private final String name; + private final String value; + private final long createTime; + + public CacheEntry(String name, String value) + { + this.value = value; + this.name = name; + this.createTime = System.currentTimeMillis(); + } + + public CacheEntry(String name, String value, long createTime) + { + this.value = value; + this.name = name; + this.createTime = createTime; + } + + public String getValue() + { + return value; + } + + public long getAge() + { + return System.currentTimeMillis() - createTime; + } + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/security/EncryptionKey.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/security/EncryptionKey.java new file mode 100644 index 0000000000..428eb3860f --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/security/EncryptionKey.java @@ -0,0 +1,73 @@ +package com.amazonaws.athena.connector.lambda.security; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.Arrays; + +public class EncryptionKey +{ + private final byte[] key; + private final byte[] nonce; + + @JsonCreator + public EncryptionKey(@JsonProperty("key") byte[] key, @JsonProperty("nonce") byte[] nonce) + { + this.key = key; + this.nonce = nonce; + } + + @JsonProperty + public byte[] getKey() + { + return key; + } + + @JsonProperty + public byte[] getNonce() + { + return nonce; + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + EncryptionKey that = (EncryptionKey) o; + + return Arrays.equals(this.key, that.key) && + Arrays.equals(this.nonce, that.nonce); + } + + @Override + public int hashCode() + { + return Arrays.hashCode(key) + 31 + Arrays.hashCode(nonce); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/security/EncryptionKeyFactory.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/security/EncryptionKeyFactory.java new file mode 100644 index 0000000000..3bc6a8548c --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/security/EncryptionKeyFactory.java @@ -0,0 +1,29 @@ +package com.amazonaws.athena.connector.lambda.security; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +public interface EncryptionKeyFactory +{ + /** + * @return A key that satisfies the specification defined in BlockCrypto + */ + EncryptionKey create(); +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/security/FederatedIdentity.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/security/FederatedIdentity.java new file mode 100644 index 0000000000..68223c5b26 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/security/FederatedIdentity.java @@ -0,0 +1,59 @@ +package com.amazonaws.athena.connector.lambda.security; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class FederatedIdentity +{ + public final String id; + public final String principal; + public final String account; + + @JsonCreator + public FederatedIdentity(@JsonProperty("id") String id, + @JsonProperty("principal") String principal, + @JsonProperty("account") String account) + { + this.id = id; + this.principal = principal; + this.account = account; + } + + @JsonProperty("id") + public String getId() + { + return id; + } + + @JsonProperty("principal") + public String getPrincipal() + { + return principal; + } + + @JsonProperty("account") + public String getAccount() + { + return account; + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/security/KmsKeyFactory.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/security/KmsKeyFactory.java new file mode 100644 index 0000000000..94456361d8 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/security/KmsKeyFactory.java @@ -0,0 +1,56 @@ +package com.amazonaws.athena.connector.lambda.security; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.services.kms.AWSKMS; +import com.amazonaws.services.kms.model.DataKeySpec; +import com.amazonaws.services.kms.model.GenerateDataKeyRequest; +import com.amazonaws.services.kms.model.GenerateDataKeyResult; +import com.amazonaws.services.kms.model.GenerateRandomRequest; +import com.amazonaws.services.kms.model.GenerateRandomResult; + +public class KmsKeyFactory + implements EncryptionKeyFactory +{ + private final AWSKMS kmsClient; + private final String masterKeyId; + + public KmsKeyFactory(AWSKMS kmsClient, String masterKeyId) + { + this.kmsClient = kmsClient; + this.masterKeyId = masterKeyId; + } + + public EncryptionKey create() + { + GenerateDataKeyResult dataKeyResult = + kmsClient.generateDataKey( + new GenerateDataKeyRequest() + .withKeyId(masterKeyId) + .withKeySpec(DataKeySpec.AES_128)); + + GenerateRandomRequest randomRequest = new GenerateRandomRequest() + .withNumberOfBytes(AesGcmBlockCrypto.NONCE_BYTES); + GenerateRandomResult randomResult = kmsClient.generateRandom(randomRequest); + + return new EncryptionKey(dataKeyResult.getPlaintext().array(), randomResult.getPlaintext().array()); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/security/LocalKeyFactory.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/security/LocalKeyFactory.java new file mode 100644 index 0000000000..5388e33936 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/security/LocalKeyFactory.java @@ -0,0 +1,47 @@ +package com.amazonaws.athena.connector.lambda.security; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; + +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; + +public class LocalKeyFactory + implements EncryptionKeyFactory +{ + public EncryptionKey create() + { + try { + SecureRandom random = SecureRandom.getInstanceStrong(); + KeyGenerator keyGen = KeyGenerator.getInstance(AesGcmBlockCrypto.KEYSPEC); + keyGen.init(AesGcmBlockCrypto.KEY_BYTES * 8, random); + SecretKey key = keyGen.generateKey(); + final byte[] nonce = new byte[AesGcmBlockCrypto.NONCE_BYTES]; + random.nextBytes(nonce); + return new EncryptionKey(key.getEncoded(), nonce); + } + catch (NoSuchAlgorithmException ex) { + throw new RuntimeException(ex); + } + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/security/NoOpBlockCrypto.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/security/NoOpBlockCrypto.java new file mode 100644 index 0000000000..15ff7de0c8 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/security/NoOpBlockCrypto.java @@ -0,0 +1,74 @@ +package com.amazonaws.athena.connector.lambda.security; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.RecordBatchSerDe; +import org.apache.arrow.vector.types.pojo.Schema; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +public class NoOpBlockCrypto + implements BlockCrypto +{ + private final RecordBatchSerDe serDe; + private final BlockAllocator allocator; + + public NoOpBlockCrypto(BlockAllocator allocator) + { + this.serDe = new RecordBatchSerDe(allocator); + this.allocator = allocator; + } + + public byte[] encrypt(EncryptionKey key, Block block) + { + if (key != null) { + throw new RuntimeException("Real key provided to NoOpBlockCrypto, likely indicates you wanted real crypto."); + } + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serDe.serialize(block.getRecordBatch(), out); + return out.toByteArray(); + } + catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + public Block decrypt(EncryptionKey key, byte[] bytes, Schema schema) + { + try { + Block resultBlock = allocator.createBlock(schema); + resultBlock.loadRecordBatch(serDe.deserialize(bytes)); + return resultBlock; + } + catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + public byte[] decrypt(EncryptionKey key, byte[] bytes) + { + return bytes; + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/serde/BlockDeserializer.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/serde/BlockDeserializer.java new file mode 100644 index 0000000000..fbe9551743 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/serde/BlockDeserializer.java @@ -0,0 +1,110 @@ +package com.amazonaws.athena.connector.lambda.serde; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorRegistry; +import com.amazonaws.athena.connector.lambda.data.RecordBatchSerDe; +import com.amazonaws.athena.connector.lambda.data.SchemaSerDe; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import org.apache.arrow.vector.ipc.message.ArrowRecordBatch; +import org.apache.arrow.vector.types.pojo.Schema; + +import java.io.ByteArrayInputStream; +import java.io.IOException; + +/** + * Uses either an explicit BlockAllocator or a BlockAllocatorRegistry to handle memory pooling associated with + * deserializing blocks. + */ +public class BlockDeserializer + extends StdDeserializer +{ + private final BlockAllocatorRegistry allocatorRegistry; + private final BlockAllocator allocator; + private final SchemaSerDe schemaSerDe; + private final RecordBatchSerDe recordBatchSerDe; + + public BlockDeserializer(BlockAllocator allocator) + { + super(Block.class); + this.schemaSerDe = new SchemaSerDe(); + this.recordBatchSerDe = new RecordBatchSerDe(allocator); + this.allocator = allocator; + this.allocatorRegistry = null; + } + + public BlockDeserializer(BlockAllocatorRegistry allocatorRegistry) + { + super(Block.class); + this.schemaSerDe = new SchemaSerDe(); + this.allocator = null; + this.recordBatchSerDe = null; + this.allocatorRegistry = allocatorRegistry; + } + + @Override + public Block deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) + throws IOException + { + JsonNode node = jsonParser.getCodec().readTree(jsonParser); + String allocatorId = node.get(BlockSerializer.ALLOCATOR_ID_FIELD_NAME).asText(); + byte[] schemaBytes = node.get(BlockSerializer.SCHEMA_FIELD_NAME).binaryValue(); + byte[] batchBytes = node.get(BlockSerializer.BATCH_FIELD_NAME).binaryValue(); + + Schema schema = schemaSerDe.deserialize(new ByteArrayInputStream(schemaBytes)); + Block block = getOrCreateAllocator(allocatorId).createBlock(schema); + + if (batchBytes.length > 0) { + ArrowRecordBatch batch = deserializeBatch(allocatorId, batchBytes); + block.loadRecordBatch(batch); + } + return block; + } + + private ArrowRecordBatch deserializeBatch(String allocatorId, byte[] batchBytes) + throws IOException + { + return getOrCreateBatchSerde(allocatorId).deserialize(batchBytes); + } + + private RecordBatchSerDe getOrCreateBatchSerde(String allocatorId) + { + if (recordBatchSerDe != null) { + return recordBatchSerDe; + } + + return new RecordBatchSerDe(getOrCreateAllocator(allocatorId)); + } + + private BlockAllocator getOrCreateAllocator(String allocatorId) + { + if (allocator != null) { + return allocator; + } + + return allocatorRegistry.getOrCreateAllocator(allocatorId); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/serde/BlockSerializer.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/serde/BlockSerializer.java new file mode 100644 index 0000000000..b10b18c512 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/serde/BlockSerializer.java @@ -0,0 +1,70 @@ +package com.amazonaws.athena.connector.lambda.serde; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.RecordBatchSerDe; +import com.amazonaws.athena.connector.lambda.data.SchemaSerDe; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +public class BlockSerializer + extends StdSerializer +{ + protected static final String ALLOCATOR_ID_FIELD_NAME = "aId"; + protected static final String SCHEMA_FIELD_NAME = "schema"; + protected static final String BATCH_FIELD_NAME = "records"; + private final SchemaSerDe schemaSerDe; + private final RecordBatchSerDe recordBatchSerDe; + + public BlockSerializer() + { + super(Block.class); + this.schemaSerDe = new SchemaSerDe(); + this.recordBatchSerDe = new RecordBatchSerDe(null); + } + + @Override + public void serialize(Block block, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) + throws IOException + { + jsonGenerator.writeStartObject(); + + jsonGenerator.writeStringField(ALLOCATOR_ID_FIELD_NAME, block.getAllocatorId()); + + ByteArrayOutputStream schemaOut = new ByteArrayOutputStream(); + schemaSerDe.serialize(block.getSchema(), schemaOut); + jsonGenerator.writeBinaryField(SCHEMA_FIELD_NAME, schemaOut.toByteArray()); + schemaOut.close(); + + ByteArrayOutputStream batchOut = new ByteArrayOutputStream(); + if (block != null && block.getRowCount() > 0) { + recordBatchSerDe.serialize(block.getRecordBatch(), batchOut); + } + jsonGenerator.writeBinaryField(BATCH_FIELD_NAME, batchOut.toByteArray()); + + jsonGenerator.writeEndObject(); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/serde/ObjectMapperFactory.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/serde/ObjectMapperFactory.java new file mode 100644 index 0000000000..f6b717bef2 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/serde/ObjectMapperFactory.java @@ -0,0 +1,51 @@ +package com.amazonaws.athena.connector.lambda.serde; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import org.apache.arrow.vector.types.pojo.Schema; + +public class ObjectMapperFactory +{ + private ObjectMapperFactory() + { + } + + public static ObjectMapper create(BlockAllocator allocator) + { + ObjectMapper objectMapper = new ObjectMapper(); + SimpleModule module = new SimpleModule(); + module.addSerializer(Schema.class, new SchemaSerializer()); + module.addDeserializer(Schema.class, new SchemaDeserializer()); + module.addDeserializer(Block.class, new BlockDeserializer(allocator)); + module.addSerializer(Block.class, new BlockSerializer()); + + //todo provide a block serializer instead of batch serializer but only serialize the batch not the schema. + objectMapper.registerModule(module) + .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY); + return objectMapper; + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/serde/SchemaDeserializer.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/serde/SchemaDeserializer.java new file mode 100644 index 0000000000..e45f6d3255 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/serde/SchemaDeserializer.java @@ -0,0 +1,52 @@ +package com.amazonaws.athena.connector.lambda.serde; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.SchemaSerDe; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import org.apache.arrow.vector.types.pojo.Schema; + +import java.io.ByteArrayInputStream; +import java.io.IOException; + +public class SchemaDeserializer + extends StdDeserializer +{ + private final SchemaSerDe serDe = new SchemaSerDe(); + + public SchemaDeserializer() + { + super(Schema.class); + } + + @Override + public Schema deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) + throws IOException, JsonProcessingException + { + JsonNode node = jsonParser.getCodec().readTree(jsonParser); + byte[] schemaBytes = node.get(SchemaSerializer.SCHEMA_FIELD_NAME).binaryValue(); + return serDe.deserialize(new ByteArrayInputStream(schemaBytes)); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/serde/SchemaSerializer.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/serde/SchemaSerializer.java new file mode 100644 index 0000000000..6ce1d43862 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/serde/SchemaSerializer.java @@ -0,0 +1,53 @@ +package com.amazonaws.athena.connector.lambda.serde; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.SchemaSerDe; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import org.apache.arrow.vector.types.pojo.Schema; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +public class SchemaSerializer + extends StdSerializer +{ + public static final String SCHEMA_FIELD_NAME = "schema"; + private final SchemaSerDe serDe = new SchemaSerDe(); + + public SchemaSerializer() + { + super(Schema.class); + } + + @Override + public void serialize(Schema schema, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) + throws IOException + { + jsonGenerator.writeStartObject(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serDe.serialize(schema, out); + jsonGenerator.writeBinaryField(SCHEMA_FIELD_NAME, out.toByteArray()); + jsonGenerator.writeEndObject(); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/udf/UserDefinedFunctionHandler.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/udf/UserDefinedFunctionHandler.java new file mode 100644 index 0000000000..a9ba0b1011 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/udf/UserDefinedFunctionHandler.java @@ -0,0 +1,227 @@ +package com.amazonaws.athena.connector.lambda.udf; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.amazonaws.athena.connector.lambda.data.BlockUtils; +import com.amazonaws.athena.connector.lambda.data.projectors.ArrowValueProjector; +import com.amazonaws.athena.connector.lambda.data.projectors.ProjectorUtils; +import com.amazonaws.athena.connector.lambda.data.writers.ArrowValueWriter; +import com.amazonaws.athena.connector.lambda.data.writers.WriterUtils; +import com.amazonaws.athena.connector.lambda.request.FederationRequest; +import com.amazonaws.athena.connector.lambda.serde.ObjectMapperFactory; +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestStreamHandler; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.Lists; +import org.apache.arrow.vector.FieldVector; +import org.apache.arrow.vector.complex.reader.FieldReader; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.arrow.vector.types.pojo.Schema; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import static com.google.common.base.Preconditions.checkState; + +/** + * Athena UDF users are expected to extend this class to create UDFs. + */ +public abstract class UserDefinedFunctionHandler implements RequestStreamHandler +{ + private static final Logger logger = LoggerFactory.getLogger(UserDefinedFunctionHandler.class); + + private static final int RETURN_COLUMN_COUNT = 1; + + @Override + public final void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) + { + try (BlockAllocator allocator = new BlockAllocatorImpl()) { + ObjectMapper objectMapper = ObjectMapperFactory.create(allocator); + try (FederationRequest rawRequest = objectMapper.readValue(inputStream, FederationRequest.class)) { + if (!(rawRequest instanceof UserDefinedFunctionRequest)) { + throw new RuntimeException("Expected a UserDefinedFunctionRequest but found " + + rawRequest.getClass()); + } + + UserDefinedFunctionRequest udfRequest = (UserDefinedFunctionRequest) rawRequest; + try (UserDefinedFunctionResponse udfResponse = processFunction(allocator, udfRequest)) { + objectMapper.writeValue(outputStream, udfResponse); + } + } + catch (Exception ex) { + throw (ex instanceof RuntimeException) ? (RuntimeException) ex : new RuntimeException(ex); + } + } + } + + @VisibleForTesting + UserDefinedFunctionResponse processFunction(BlockAllocator allocator, UserDefinedFunctionRequest req) + { + UserDefinedFunctionType functionType = req.getFunctionType(); + switch (functionType) { + case SCALAR: + return processScalarFunction(allocator, req); + default: + throw new UnsupportedOperationException("Unsupported function type " + functionType); + } + } + + private UserDefinedFunctionResponse processScalarFunction(BlockAllocator allocator, UserDefinedFunctionRequest req) + { + Method udfMethod = extractScalarFunctionMethod(req); + Block inputRecords = req.getInputRecords(); + Schema outputSchema = req.getOutputSchema(); + + Block outputRecords = processRows(allocator, udfMethod, inputRecords, outputSchema); + return new UserDefinedFunctionResponse(outputRecords, udfMethod.getName()); + } + + /** + * Processes a group by rows. This method takes in a block of data (containing multiple rows), process them and + * returns multiple rows of the output column in a block. + * + * UDF methods are invoked row-by-row in a for loop. Arrow values are converted to Java Objects and then passed into + * the UDF java method. This is not very efficient because we might potentially be doing a lot of data copying. + * Advanced users could choose to override this method and directly deal with Arrow data to achieve better + * performance. + * + * @param allocator arrow memory allocator + * @param udfMethod the extracted java method matching the User-Defined-Function defined in Athena. + * @param inputRecords input data in Arrow format + * @param outputSchema output data schema in Arrow format + * @return output data in Arrow format + */ + protected Block processRows(BlockAllocator allocator, Method udfMethod, Block inputRecords, Schema outputSchema) + { + int rowCount = inputRecords.getRowCount(); + + List valueProjectors = Lists.newArrayList(); + + for (Field field : inputRecords.getFields()) { + FieldReader fieldReader = inputRecords.getFieldReader(field.getName()); + ArrowValueProjector arrowValueProjector = ProjectorUtils.createArrowValueProjector(fieldReader); + valueProjectors.add(arrowValueProjector); + } + + Block outputRecords = allocator.createBlock(outputSchema); + outputRecords.setRowCount(rowCount); + + try { + String outputFieldName = outputSchema.getFields().get(0).getName(); + FieldVector outputVector = outputRecords.getFieldVector(outputFieldName); + ArrowValueWriter outputProjector = WriterUtils.createArrowValueWriter(outputVector); + Object[] arguments = new Object[valueProjectors.size()]; + + for (int rowNum = 0; rowNum < rowCount; ++rowNum) { + for (int col = 0; col < valueProjectors.size(); ++col) { + arguments[col] = valueProjectors.get(col).project(rowNum); + } + + try { + Object result = udfMethod.invoke(this, arguments); + outputProjector.write(rowNum, result); + } + catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + catch (IllegalArgumentException e) { + String msg = String.format("%s. Expected function types %s, got types %s", + e.getMessage(), + Arrays.stream(udfMethod.getParameterTypes()).map(clazz -> clazz.getName()).collect(Collectors.toList()), + Arrays.stream(arguments).map(arg -> arg.getClass().getName()).collect(Collectors.toList())); + throw new RuntimeException(msg, e); + } + } + } + catch (Throwable t) { + try { + outputRecords.close(); + } + catch (Exception e) { + logger.error("Error closing output block", e); + } + throw t; + } + + return outputRecords; + } + + /** + * Use reflection to find tha java method that maches the UDF function defined in Athena SQL. + * @param req UDF request + * @return java method matching the UDF defined in Athena query. + */ + private Method extractScalarFunctionMethod(UserDefinedFunctionRequest req) + { + String methodName = req.getMethodName(); + Class[] argumentTypes = extractJavaTypes(req.getInputRecords().getSchema()); + Class[] returnTypes = extractJavaTypes(req.getOutputSchema()); + checkState(returnTypes.length == RETURN_COLUMN_COUNT, + String.format("Expecting %d return columns, found %d in method signature.", + RETURN_COLUMN_COUNT, returnTypes.length)); + Class returnType = returnTypes[0]; + + Method udfMethod; + try { + udfMethod = this.getClass().getMethod(methodName, argumentTypes); + logger.info(String.format("Found UDF method %s with input types [%s] and output types [%s]", + methodName, Arrays.toString(argumentTypes), returnType.getName())); + } + catch (NoSuchMethodException e) { + String msg = "Failed to find UDF method. " + e.getMessage() + + " Please make sure the method name contains only lowercase and the method signature (name and" + + " argument types) in Lambda matches the function signature defined in SQL."; + throw new RuntimeException(msg, e); + } + + if (!returnType.equals(udfMethod.getReturnType())) { + throw new IllegalArgumentException("signature return type " + returnType + + " does not match udf implementation return type " + udfMethod.getReturnType()); + } + + return udfMethod; + } + + private Class[] extractJavaTypes(Schema schema) + { + Class[] types = new Class[schema.getFields().size()]; + + List fields = schema.getFields(); + for (int i = 0; i < fields.size(); ++i) { + Types.MinorType minorType = Types.getMinorTypeForArrowType(fields.get(i).getType()); + types[i] = BlockUtils.getJavaType(minorType); + } + + return types; + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/udf/UserDefinedFunctionRequest.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/udf/UserDefinedFunctionRequest.java new file mode 100644 index 0000000000..64951c0535 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/udf/UserDefinedFunctionRequest.java @@ -0,0 +1,105 @@ +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connector.lambda.udf; + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.request.FederationRequest; +import com.amazonaws.athena.connector.lambda.security.FederatedIdentity; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.apache.arrow.vector.types.pojo.Schema; + +import java.util.Objects; + +import static java.util.Objects.requireNonNull; + +public class UserDefinedFunctionRequest extends FederationRequest +{ + private final Block inputRecords; + private final Schema outputSchema; + private final String methodName; + private final UserDefinedFunctionType functionType; + + @JsonCreator + public UserDefinedFunctionRequest(@JsonProperty("identity") FederatedIdentity identity, + @JsonProperty("inputRecords") Block inputRecords, + @JsonProperty("outputSchema") Schema outputSchema, + @JsonProperty("methodName") String methodName, + @JsonProperty("functionType") UserDefinedFunctionType functionType) + { + super(identity); + this.inputRecords = requireNonNull(inputRecords, "inputRecords is null"); + this.outputSchema = requireNonNull(outputSchema, "outputSchema is null"); + this.methodName = requireNonNull(methodName, "methodName is null"); + this.functionType = requireNonNull(functionType, "functionType is null"); + } + + @Override + public void close() throws Exception + { + inputRecords.close(); + } + + @JsonProperty("inputRecords") + public Block getInputRecords() + { + return inputRecords; + } + + @JsonProperty("outputSchema") + public Schema getOutputSchema() + { + return outputSchema; + } + + @JsonProperty("methodName") + public String getMethodName() + { + return methodName; + } + + @JsonProperty("functionType") + public UserDefinedFunctionType getFunctionType() + { + return functionType; + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (!(o instanceof UserDefinedFunctionRequest)) { + return false; + } + UserDefinedFunctionRequest that = (UserDefinedFunctionRequest) o; + return getInputRecords().equals(that.getInputRecords()) && + getOutputSchema().equals(that.getOutputSchema()) && + getMethodName().equals(that.getMethodName()) && + getFunctionType() == that.getFunctionType(); + } + + @Override + public int hashCode() + { + return Objects.hash(getInputRecords(), getOutputSchema(), getMethodName(), getFunctionType()); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/udf/UserDefinedFunctionResponse.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/udf/UserDefinedFunctionResponse.java new file mode 100644 index 0000000000..466dad6033 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/udf/UserDefinedFunctionResponse.java @@ -0,0 +1,59 @@ +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connector.lambda.udf; + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.request.FederationResponse; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import static java.util.Objects.requireNonNull; + +public class UserDefinedFunctionResponse extends FederationResponse +{ + private final Block records; + private final String methodName; + + @JsonCreator + public UserDefinedFunctionResponse(@JsonProperty("records") Block records, + @JsonProperty("methodName") String methodName) + { + this.records = requireNonNull(records, "records is null"); + this.methodName = requireNonNull(methodName, "methodName is null"); + } + + @JsonProperty("records") + public Block getRecords() + { + return records; + } + + @JsonProperty("methodName") + public String getMethodName() + { + return methodName; + } + + @Override + public void close() throws Exception + { + records.close(); + } +} diff --git a/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/udf/UserDefinedFunctionType.java b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/udf/UserDefinedFunctionType.java new file mode 100644 index 0000000000..b0a108fac4 --- /dev/null +++ b/athena-federation-sdk/src/main/java/com/amazonaws/athena/connector/lambda/udf/UserDefinedFunctionType.java @@ -0,0 +1,25 @@ +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connector.lambda.udf; + +public enum UserDefinedFunctionType +{ + SCALAR +} diff --git a/athena-federation-sdk/src/main/resources/log4j.properties b/athena-federation-sdk/src/main/resources/log4j.properties new file mode 100644 index 0000000000..15b502c445 --- /dev/null +++ b/athena-federation-sdk/src/main/resources/log4j.properties @@ -0,0 +1,26 @@ +### +# #%L +# Amazon Athena Query Federation SDK +# %% +# Copyright (C) 2019 Amazon Web Services +# %% +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# #L% +### +log = . +log4j.rootLogger = INFO, LAMBDA + +#Define the LAMBDA appender +log4j.appender.LAMBDA=com.amazonaws.services.lambda.runtime.log4j.LambdaAppender +log4j.appender.LAMBDA.layout=org.apache.log4j.PatternLayout +log4j.appender.LAMBDA.layout.conversionPattern=%d{yyyy-MM-dd HH:mm:ss} <%X{AWSRequestId}> %-5p %c{1}:%m%n diff --git a/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/QueryStatusCheckerTest.java b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/QueryStatusCheckerTest.java new file mode 100644 index 0000000000..599759b661 --- /dev/null +++ b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/QueryStatusCheckerTest.java @@ -0,0 +1,119 @@ +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connector.lambda; + +import com.amazonaws.AmazonServiceException; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.athena.model.GetQueryExecutionRequest; +import com.amazonaws.services.athena.model.GetQueryExecutionResult; +import com.amazonaws.services.athena.model.InvalidRequestException; +import com.amazonaws.services.athena.model.QueryExecution; +import com.amazonaws.services.athena.model.QueryExecutionStatus; +import com.google.common.collect.ImmutableList; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.stubbing.OngoingStubbing; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; +import java.util.Random; + +import static com.amazonaws.athena.connector.lambda.handlers.AthenaExceptionFilter.ATHENA_EXCEPTION_FILTER; +import static org.junit.Assert.*; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class QueryStatusCheckerTest +{ + private final ThrottlingInvoker athenaInvoker = ThrottlingInvoker.newDefaultBuilder(ATHENA_EXCEPTION_FILTER).build(); + + @Mock + private AmazonAthena athena; + + @Test + public void testFastTermination() + throws InterruptedException + { + String queryId = "query0"; + GetQueryExecutionRequest request = new GetQueryExecutionRequest().withQueryExecutionId(queryId); + when(athena.getQueryExecution(request)).thenReturn(new GetQueryExecutionResult().withQueryExecution(new QueryExecution().withStatus(new QueryExecutionStatus().withState("FAILED")))); + QueryStatusChecker queryStatusChecker = new QueryStatusChecker(athena, athenaInvoker, queryId); + assertTrue(queryStatusChecker.isQueryRunning()); + Thread.sleep(2000); + assertFalse(queryStatusChecker.isQueryRunning()); + verify(athena, times(1)).getQueryExecution(any()); + } + + @Test + public void testSlowTermination() + throws InterruptedException + { + String queryId = "query1"; + GetQueryExecutionRequest request = new GetQueryExecutionRequest().withQueryExecutionId(queryId); + GetQueryExecutionResult result1and2 = new GetQueryExecutionResult().withQueryExecution(new QueryExecution().withStatus(new QueryExecutionStatus().withState("RUNNING"))); + GetQueryExecutionResult result3 = new GetQueryExecutionResult().withQueryExecution(new QueryExecution().withStatus(new QueryExecutionStatus().withState("SUCCEEDED"))); + when(athena.getQueryExecution(request)).thenReturn(result1and2).thenReturn(result1and2).thenReturn(result3); + try (QueryStatusChecker queryStatusChecker = new QueryStatusChecker(athena, athenaInvoker, queryId)) { + assertTrue(queryStatusChecker.isQueryRunning()); + Thread.sleep(2000); + assertTrue(queryStatusChecker.isQueryRunning()); + Thread.sleep(3000); + assertFalse(queryStatusChecker.isQueryRunning()); + verify(athena, times(3)).getQueryExecution(any()); + } + } + + @Test + public void testNotFound() + throws InterruptedException + { + String queryId = "query2"; + GetQueryExecutionRequest request = new GetQueryExecutionRequest().withQueryExecutionId(queryId); + when(athena.getQueryExecution(request)).thenThrow(new InvalidRequestException("")); + try (QueryStatusChecker queryStatusChecker = new QueryStatusChecker(athena, athenaInvoker, queryId)) { + assertTrue(queryStatusChecker.isQueryRunning()); + Thread.sleep(2000); + assertTrue(queryStatusChecker.isQueryRunning()); + verify(athena, times(1)).getQueryExecution(any()); + } + } + + @Test + public void testOtherError() + throws InterruptedException + { + String queryId = "query3"; + GetQueryExecutionRequest request = new GetQueryExecutionRequest().withQueryExecutionId(queryId); + when(athena.getQueryExecution(request)).thenThrow(new AmazonServiceException("")); + try (QueryStatusChecker queryStatusChecker = new QueryStatusChecker(athena, athenaInvoker, queryId)) { + assertTrue(queryStatusChecker.isQueryRunning()); + Thread.sleep(3000); + assertTrue(queryStatusChecker.isQueryRunning()); + verify(athena, times(2)).getQueryExecution(any()); + } + } +} diff --git a/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/ThrottlingInvokerTest.java b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/ThrottlingInvokerTest.java new file mode 100644 index 0000000000..a15093c5d1 --- /dev/null +++ b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/ThrottlingInvokerTest.java @@ -0,0 +1,122 @@ +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connector.lambda; + +import com.amazonaws.athena.connector.lambda.data.BlockSpiller; +import com.amazonaws.athena.connector.lambda.exceptions.FederationThrottleException; +import org.junit.Test; + +import java.sql.Time; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicLong; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ThrottlingInvokerTest +{ + + @Test + public void invokeNoThrottle() + throws TimeoutException + { + ThrottlingInvoker invoker = ThrottlingInvoker.newBuilder() + .withDecrease(0.5) + .withIncrease(10) + .withInitialDelayMs(10) + .withMaxDelayMs(2_000) + .withFilter((Exception ex) -> ex instanceof FederationThrottleException) + .build(); + + for (int i = 0; i < 100; i++) { + //Make a call and validate that the state didn't change + int result = invoker.invoke(() -> 1 + 1, 10_000); + assertEquals(2, result); + assertEquals(ThrottlingInvoker.State.FAST_START, invoker.getState()); + assertEquals(0, invoker.getDelay()); + } + } + + @Test + public void invokeWithThrottle() + throws TimeoutException + { + ThrottlingInvoker invoker = ThrottlingInvoker.newBuilder() + .withDecrease(0.8) + .withIncrease(1) + .withInitialDelayMs(10) + .withMaxDelayMs(200) + .withFilter((Exception ex) -> ex instanceof FederationThrottleException) + .build(); + + for (int i = 0; i < 5; i++) { + //Make a call and validate that the state didn't change + final AtomicLong count = new AtomicLong(0); + final int val = i; + long result = invoker.invoke(() -> { + if (count.incrementAndGet() < 4) { + throw new FederationThrottleException(); + } + return val; + } + , 10_000); + assertEquals(val, result); + assertEquals(4, count.get()); + assertEquals(ThrottlingInvoker.State.AVOIDANCE, invoker.getState()); + assertTrue(invoker.getDelay() > 0); + } + + assertEquals(199, invoker.getDelay()); + } + + @Test(expected = TimeoutException.class) + public void invokeWithThrottleTimeout() + throws TimeoutException + { + ThrottlingInvoker invoker = ThrottlingInvoker.newBuilder() + .withDecrease(0.5) + .withIncrease(10) + .withInitialDelayMs(10) + .withMaxDelayMs(500) + .withFilter((Exception ex) -> ex instanceof FederationThrottleException) + .build(); + + invoker.invoke(() -> {throw new FederationThrottleException();}, 2_000); + } + + @Test(expected = FederationThrottleException.class) + public void invokeWithThrottleNoSpill() + throws TimeoutException + { + BlockSpiller spiller = mock(BlockSpiller.class); + ThrottlingInvoker invoker = ThrottlingInvoker.newBuilder() + .withDecrease(0.5) + .withIncrease(10) + .withInitialDelayMs(10) + .withMaxDelayMs(500) + .withFilter((Exception ex) -> ex instanceof RuntimeException) + .withSpiller(spiller) + .build(); + + when(spiller.spilled()).thenReturn(false); + invoker.invoke(() -> {throw new RuntimeException();}, 2_000); + } +} diff --git a/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/data/BlockTest.java b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/data/BlockTest.java new file mode 100644 index 0000000000..a226f6d581 --- /dev/null +++ b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/data/BlockTest.java @@ -0,0 +1,858 @@ +package com.amazonaws.athena.connector.lambda.data; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.domain.predicate.ConstraintEvaluator; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.domain.predicate.EquatableValueSet; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import com.amazonaws.auth.AWSCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.auth.BasicSessionCredentials; +import com.amazonaws.internal.StaticCredentialsProvider; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.amazonaws.services.secretsmanager.AWSSecretsManagerClientBuilder; +import com.amazonaws.services.secretsmanager.model.GetSecretValueRequest; +import com.amazonaws.services.secretsmanager.model.GetSecretValueResult; +import org.apache.arrow.vector.BigIntVector; +import org.apache.arrow.vector.BitVector; +import org.apache.arrow.vector.DateDayVector; +import org.apache.arrow.vector.DateMilliVector; +import org.apache.arrow.vector.DecimalVector; +import org.apache.arrow.vector.Float4Vector; +import org.apache.arrow.vector.Float8Vector; +import org.apache.arrow.vector.IntVector; +import org.apache.arrow.vector.SmallIntVector; +import org.apache.arrow.vector.TinyIntVector; +import org.apache.arrow.vector.UInt1Vector; +import org.apache.arrow.vector.UInt2Vector; +import org.apache.arrow.vector.UInt4Vector; +import org.apache.arrow.vector.UInt8Vector; +import org.apache.arrow.vector.ValueVector; +import org.apache.arrow.vector.VarBinaryVector; +import org.apache.arrow.vector.VarCharVector; +import org.apache.arrow.vector.complex.ListVector; +import org.apache.arrow.vector.complex.StructVector; +import org.apache.arrow.vector.complex.reader.FieldReader; +import org.apache.arrow.vector.complex.reader.Float8Reader; +import org.apache.arrow.vector.complex.reader.IntReader; +import org.apache.arrow.vector.complex.reader.VarCharReader; +import org.apache.arrow.vector.ipc.message.ArrowRecordBatch; +import org.apache.arrow.vector.types.DateUnit; +import org.apache.arrow.vector.types.FloatingPointPrecision; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.arrow.vector.types.pojo.Schema; +import org.apache.arrow.vector.util.Text; +import org.apache.commons.codec.Charsets; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.activation.UnsupportedDataTypeException; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +//TODO: Consider breaking up this test into 3 separate tests but the setup for the test would be error prone +// Also having them condensed like this gives a good example for how to use Apache Arrow. +public class BlockTest +{ + private static final Logger logger = LoggerFactory.getLogger(BlockTest.class); + private BlockAllocatorImpl allocator; + + @Before + public void setup() + { + allocator = new BlockAllocatorImpl(); + } + + @After + public void tearDown() + { + allocator.close(); + } + + @Test + public void constrainedBlockTest() + throws Exception + { + Schema schema = SchemaBuilder.newBuilder() + .addIntField("col1") + .addIntField("col2") + .build(); + + Block block = allocator.createBlock(schema); + + ValueSet col1Constraint = EquatableValueSet.newBuilder(allocator, Types.MinorType.INT.getType(), true, false) + .add(10).build(); + Constraints constraints = new Constraints(Collections.singletonMap("col1", col1Constraint)); + try (ConstraintEvaluator constraintEvaluator = new ConstraintEvaluator(allocator, schema, constraints)) { + block.constrain(constraintEvaluator); + assertTrue(block.setValue("col1", 0, 10)); + assertTrue(block.offerValue("col1", 0, 10)); + assertFalse(block.setValue("col1", 0, 11)); + assertFalse(block.offerValue("col1", 0, 11)); + assertTrue(block.offerValue("unkown_col", 0, 10)); + } + } + + //TODO: Break this into multiple smaller tests, probably primitive types vs. complex vs. nested complex + //TODO: List of Lists + //TODO: List of Structs + @Test + public void EndToEndBlockTest() + throws Exception + { + BlockAllocatorImpl expectedAllocator = new BlockAllocatorImpl(); + + int expectedRows = 20; + + Schema origSchema = generateTestSchema(); + Block expectedBlock = generateTestBlock(expectedAllocator, origSchema, expectedRows); + + RecordBatchSerDe expectSerDe = new RecordBatchSerDe(expectedAllocator); + ByteArrayOutputStream blockOut = new ByteArrayOutputStream(); + ArrowRecordBatch expectedBatch = expectedBlock.getRecordBatch(); + expectSerDe.serialize(expectedBatch, blockOut); + expectedBatch.close(); + expectedBlock.close(); + + ByteArrayOutputStream schemaOut = new ByteArrayOutputStream(); + SchemaSerDe schemaSerDe = new SchemaSerDe(); + schemaSerDe.serialize(origSchema, schemaOut); + Schema actualSchema = schemaSerDe.deserialize(new ByteArrayInputStream(schemaOut.toByteArray())); + + BlockAllocatorImpl actualAllocator = new BlockAllocatorImpl(); + RecordBatchSerDe actualSerDe = new RecordBatchSerDe(actualAllocator); + ArrowRecordBatch batch = actualSerDe.deserialize(blockOut.toByteArray()); + + /** + * Generate and write the block + */ + Block actualBlock = actualAllocator.createBlock(actualSchema); + actualBlock.loadRecordBatch(batch); + batch.close(); + + for (int i = 0; i < actualBlock.getRowCount(); i++) { + logger.info("EndToEndBlockTest: util {}", BlockUtils.rowToString(actualBlock, i)); + } + + assertEquals("Row count missmatch", expectedRows, actualBlock.getRowCount()); + int actualFieldCount = 1; + for (Field next : actualBlock.getFields()) { + FieldReader vector = actualBlock.getFieldReader(next.getName()); + switch (vector.getMinorType()) { + case INT: + IntReader intVector = vector; + for (int i = 0; i < actualBlock.getRowCount(); i++) { + intVector.setPosition(i); + assertEquals(i * actualFieldCount * 3, intVector.readInteger().intValue()); + } + break; + case FLOAT8: + Float8Reader fVector = vector; + for (int i = 0; i < actualBlock.getRowCount(); i++) { + fVector.setPosition(i); + assertEquals(i * actualFieldCount * 1.1, fVector.readDouble().doubleValue(), .1); + } + break; + case VARCHAR: + VarCharReader vVector = (VarCharReader) vector; + for (int i = 0; i < actualBlock.getRowCount(); i++) { + vVector.setPosition(i); + assertEquals(String.valueOf(i * actualFieldCount), vVector.readText().toString()); + } + break; + case STRUCT: + for (int i = 0; i < actualBlock.getRowCount(); i++) { + FieldReader effectiveVector = vector; + if (vector.getField().getName().equals("structFieldNested28")) { + //If this is our struct with a nested struct, then grab the nested struct and check it + effectiveVector = vector.reader("nestedStruct"); + } + effectiveVector.setPosition(i); + assertEquals(" name: " + effectiveVector.getField().getName(), Long.valueOf(i), effectiveVector.reader("nestedBigInt").readLong()); + assertEquals(String.valueOf(1000 + i), effectiveVector.reader("nestedString").readText().toString()); + } + break; + case LIST: + int actual = 0; + Field child = vector.getField().getChildren().get(0); + for (int i = 0; i < actualBlock.getRowCount(); i++) { + vector.setPosition(i); + int entryValues = 0; + while (vector.next()) { + if (Types.getMinorTypeForArrowType(child.getType()) == Types.MinorType.BIGINT) { + assertEquals(Long.valueOf(i + entryValues++), vector.reader().readLong()); + } + else if (Types.getMinorTypeForArrowType(child.getType()) == Types.MinorType.VARCHAR) { + assertEquals(String.valueOf(1000 + i + entryValues++), vector.reader().readText().toString()); + } + else if (Types.getMinorTypeForArrowType(child.getType()) == Types.MinorType.SMALLINT) { + entryValues++; + assertTrue((new Integer(i + 1)).shortValue() == vector.reader().readShort()); + } + else if (Types.getMinorTypeForArrowType(child.getType()) == Types.MinorType.INT) { + entryValues++; + assertTrue(i == vector.reader().readInteger()); + } + else if (Types.getMinorTypeForArrowType(child.getType()) == Types.MinorType.TINYINT) { + entryValues++; + assertTrue((byte) i == vector.reader().readByte()); + } + else if (Types.getMinorTypeForArrowType(child.getType()) == Types.MinorType.FLOAT4) { + entryValues++; + assertTrue(i * 1.0F == vector.reader().readFloat()); + } + else if (Types.getMinorTypeForArrowType(child.getType()) == Types.MinorType.FLOAT8) { + entryValues++; + assertTrue(i * 1.0D == vector.reader().readDouble()); + } + else if (Types.getMinorTypeForArrowType(child.getType()) == Types.MinorType.DECIMAL) { + entryValues++; + assertTrue(i * 100L == vector.reader().readBigDecimal().unscaledValue().longValue()); + } + else if (Types.getMinorTypeForArrowType(child.getType()) == Types.MinorType.VARBINARY) { + entryValues++; + //no comparing + } + else if (Types.getMinorTypeForArrowType(child.getType()) == Types.MinorType.BIT) { + entryValues++; + assertTrue((i % 2 == 1) == vector.reader().readBoolean()); + } + } + if (entryValues > 0) {actual++;} + } + + assertEquals("failed for " + vector.getField().getName(), actualBlock.getRowCount(), actual); + break; + default: + //todo: add more types here. + } + actualFieldCount++; + } + + /** + * Now check that we can unset a row properly + */ + BlockUtils.unsetRow(0, actualBlock); + + for (Field next : actualBlock.getFields()) { + FieldReader vector = actualBlock.getFieldReader(next.getName()); + switch (vector.getMinorType()) { + case DATEDAY: + case DATEMILLI: + case TINYINT: + case UINT1: + case SMALLINT: + case UINT2: + case UINT4: + case INT: + case UINT8: + case BIGINT: + case FLOAT4: + case FLOAT8: + case DECIMAL: + case VARBINARY: + case VARCHAR: + case BIT: + case STRUCT: + vector.setPosition(0); + assertFalse("Failed for " + vector.getMinorType() + " " + next.getName(), vector.isSet()); + break; + case LIST: + //no supported for unsetRow(...) this is a TODO to see if its possible some other way + break; + default: + throw new UnsupportedDataTypeException(next.getType().getTypeID() + " is not supported"); + } + actualFieldCount++; + } + + logger.info("EndToEndBlockTest: block size {}", actualAllocator.getUsage()); + actualBlock.close(); + } + + public static Schema generateTestSchema() { + /** + * Generate and write the schema + */ + SchemaBuilder schemaBuilder = new SchemaBuilder(); + schemaBuilder.addMetadata("meta1", "meta-value-1"); + schemaBuilder.addMetadata("meta2", "meta-value-2"); + schemaBuilder.addField("intfield1", new ArrowType.Int(32, true)); + schemaBuilder.addField("doublefield2", new ArrowType.FloatingPoint(FloatingPointPrecision.DOUBLE)); + schemaBuilder.addField("varcharfield3", new ArrowType.Utf8()); + + schemaBuilder.addField("datemillifield4", new ArrowType.Date(DateUnit.MILLISECOND)); + schemaBuilder.addField("tinyintfield5", new ArrowType.Int(8, true)); + schemaBuilder.addField("uint1field6", new ArrowType.Int(8, false)); + schemaBuilder.addField("smallintfield7", new ArrowType.Int(16, true)); + schemaBuilder.addField("uint2field8", new ArrowType.Int(16, false)); + schemaBuilder.addField("datedayfield9", new ArrowType.Date(DateUnit.DAY)); + schemaBuilder.addField("uint4field10", new ArrowType.Int(32, false)); + schemaBuilder.addField("bigintfield11", new ArrowType.Int(64, true)); + schemaBuilder.addField("decimalfield12", new ArrowType.Decimal(10, 2)); + schemaBuilder.addField("floatfield13", new ArrowType.FloatingPoint(FloatingPointPrecision.SINGLE)); + schemaBuilder.addField("varbinaryfield14", new ArrowType.Binary()); + schemaBuilder.addField("bitfield15", new ArrowType.Bool()); + + schemaBuilder.addListField("varcharlist16", Types.MinorType.VARCHAR.getType()); + schemaBuilder.addListField("intlist17", Types.MinorType.INT.getType()); + schemaBuilder.addListField("bigintlist18", Types.MinorType.BIGINT.getType()); + schemaBuilder.addListField("tinyintlist19", Types.MinorType.TINYINT.getType()); + schemaBuilder.addListField("smallintlist20", Types.MinorType.SMALLINT.getType()); + schemaBuilder.addListField("float4list21", Types.MinorType.FLOAT4.getType()); + schemaBuilder.addListField("float8list22", Types.MinorType.FLOAT8.getType()); + schemaBuilder.addListField("shortdeclist23", new ArrowType.Decimal(10, 2)); + schemaBuilder.addListField("londdeclist24", new ArrowType.Decimal(21, 2)); + schemaBuilder.addListField("varbinarylist25", Types.MinorType.VARBINARY.getType()); + schemaBuilder.addListField("bitlist26", Types.MinorType.BIT.getType()); + + schemaBuilder.addStructField("structField27"); + schemaBuilder.addChildField("structField27", "nestedBigInt", Types.MinorType.BIGINT.getType()); + schemaBuilder.addChildField("structField27", "nestedString", Types.MinorType.VARCHAR.getType()); + schemaBuilder.addChildField("structField27", "tinyintcol", Types.MinorType.TINYINT.getType()); + schemaBuilder.addChildField("structField27", "smallintcol", Types.MinorType.SMALLINT.getType()); + schemaBuilder.addChildField("structField27", "float4Col", Types.MinorType.FLOAT4.getType()); + schemaBuilder.addChildField("structField27", "float8Col", Types.MinorType.FLOAT8.getType()); + schemaBuilder.addChildField("structField27", "shortDecCol", new ArrowType.Decimal(10, 2)); + schemaBuilder.addChildField("structField27", "longDecCol", new ArrowType.Decimal(21, 2)); + schemaBuilder.addChildField("structField27", "binaryCol", Types.MinorType.VARBINARY.getType()); + schemaBuilder.addChildField("structField27", "bitCol", Types.MinorType.BIT.getType()); + schemaBuilder.addStructField("structFieldNested28"); + schemaBuilder.addChildField("structFieldNested28", "bitCol", Types.MinorType.BIT.getType()); + schemaBuilder.addChildField("structFieldNested28", + FieldBuilder.newBuilder("nestedStruct", new ArrowType.Struct()) + .addField("nestedString", Types.MinorType.VARCHAR.getType(), null) + .addField("nestedBigInt", Types.MinorType.BIGINT.getType(), null) + .addListField("nestedList", Types.MinorType.VARCHAR.getType()) + .addListField("nestedListDec", new ArrowType.Decimal(10, 2)) + .build()); + return schemaBuilder.build(); + } + + + public static Block generateTestBlock(BlockAllocatorImpl expectedAllocator, Schema origSchema, int expectedRows) throws UnsupportedDataTypeException { + /** + * Generate and write the block + */ + Block expectedBlock = expectedAllocator.createBlock(origSchema); + int fieldCount = 1; + for (Field next : origSchema.getFields()) { + ValueVector vector = expectedBlock.getFieldVector(next.getName()); + switch (vector.getMinorType()) { + case DATEDAY: + DateDayVector dateDayVector = (DateDayVector) vector; + for (int i = 0; i < expectedRows; i++) { + dateDayVector.setSafe(i, i * fieldCount); + } + break; + case UINT4: + UInt4Vector uInt4Vector = (UInt4Vector) vector; + for (int i = 0; i < expectedRows; i++) { + uInt4Vector.setSafe(i, i * fieldCount * 2); + } + break; + case INT: + IntVector intVector = (IntVector) vector; + for (int i = 0; i < expectedRows; i++) { + intVector.setSafe(i, i * fieldCount * 3); + } + break; + case FLOAT8: + Float8Vector fVector = (Float8Vector) vector; + for (int i = 0; i < expectedRows; i++) { + fVector.setSafe(i, i * fieldCount * 1.1); + } + break; + case VARCHAR: + VarCharVector vVector = (VarCharVector) vector; + for (int i = 0; i < expectedRows; i++) { + vVector.setSafe(i, String.valueOf(i * fieldCount).getBytes(Charsets.UTF_8)); + } + break; + case DATEMILLI: + DateMilliVector dateMilliVector = (DateMilliVector) vector; + for (int i = 0; i < expectedRows; i++) { + dateMilliVector.setSafe(i, i * fieldCount * 4); + } + break; + case TINYINT: + TinyIntVector tinyIntVector = (TinyIntVector) vector; + for (int i = 0; i < expectedRows; i++) { + tinyIntVector.setSafe(i, i * fieldCount * 5); + } + break; + case UINT1: + UInt1Vector uInt1Vector = (UInt1Vector) vector; + for (int i = 0; i < expectedRows; i++) { + uInt1Vector.setSafe(i, i * fieldCount * 6); + } + break; + case SMALLINT: + SmallIntVector smallIntVector = (SmallIntVector) vector; + for (int i = 0; i < expectedRows; i++) { + smallIntVector.setSafe(i, i * fieldCount * 7); + } + break; + case UINT2: + UInt2Vector uInt2Vector = (UInt2Vector) vector; + for (int i = 0; i < expectedRows; i++) { + uInt2Vector.setSafe(i, i * fieldCount * 8); + } + break; + case UINT8: + UInt8Vector uInt8Vector = (UInt8Vector) vector; + for (int i = 0; i < expectedRows; i++) { + uInt8Vector.setSafe(i, i * fieldCount * 9); + } + break; + case BIGINT: + BigIntVector bigIntVector = (BigIntVector) vector; + for (int i = 0; i < expectedRows; i++) { + bigIntVector.setSafe(i, i * fieldCount * 10); + } + break; + case DECIMAL: + DecimalVector decimalVector = (DecimalVector) vector; + for (int i = 0; i < expectedRows; i++) { + BigDecimal bigDecimal = new BigDecimal((double) (i * fieldCount) * 1.01); + bigDecimal = bigDecimal.setScale(2, RoundingMode.HALF_UP); + decimalVector.setSafe(i, bigDecimal); + } + break; + case FLOAT4: + Float4Vector float4Vector = (Float4Vector) vector; + for (int i = 0; i < expectedRows; i++) { + float4Vector.setSafe(i, i * fieldCount * 9); + } + break; + case VARBINARY: + VarBinaryVector varBinaryVector = (VarBinaryVector) vector; + for (int i = 0; i < expectedRows; i++) { + byte[] data = String.valueOf(i * fieldCount).getBytes(); + varBinaryVector.setSafe(i, data); + } + break; + case BIT: + BitVector bitVector = (BitVector) vector; + for (int i = 0; i < expectedRows; i++) { + bitVector.setSafe(i, i % 2); + } + break; + case STRUCT: + StructVector sVector = (StructVector) vector; + for (int i = 0; i < expectedRows; i++) { + final int seed = i; + BlockUtils.setComplexValue(sVector, i, (Field field, Object value) -> { + if (field.getName().equals("nestedBigInt")) { + return (long) seed; + } + if (field.getName().equals("nestedString")) { + return String.valueOf(1000 + seed); + } + if (field.getName().equals("tinyintcol")) { + return (byte) seed; + } + + if (field.getName().equals("smallintcol")) { + return (short) seed; + } + + if (field.getName().equals("nestedList")) { + List values = new ArrayList<>(); + values.add("val1"); + values.add("val2"); + return values; + } + + if (field.getName().equals("nestedListDec")) { + List values = new ArrayList<>(); + values.add(2.0D); + values.add(2.2D); + return values; + } + + if (field.getName().equals("float4Col")) { + return seed * 1.0F; + } + if (field.getName().equals("float8Col")) { + return seed * 2.0D; + } + if (field.getName().equals("shortDecCol")) { + return seed * 3.0D; + } + if (field.getName().equals("longDecCol")) { + return seed * 4.0D; + } + if (field.getName().equals("binaryCol")) { + return String.valueOf(seed).getBytes(Charsets.UTF_8); + } + if (field.getName().equals("bitCol")) { + return seed % 2 == 1; + } + if (field.getName().equals("nestedStruct")) { + //doesn't matter since we are generating the values for the struct + //it just needs to be non-null + return new Object(); + } + + throw new RuntimeException("Unexpected field " + field.getName()); + }, new Object()); + } + break; + case LIST: + Field child = vector.getField().getChildren().get(0); + + if (Types.getMinorTypeForArrowType(child.getType()) == Types.MinorType.BIGINT) { + for (int i = 0; i < expectedRows; i++) { + List values = new ArrayList<>(); + values.add(Long.valueOf(i)); + values.add(i + 1L); + values.add(i + 2L); + BlockUtils.setComplexValue((ListVector) vector, i, + FieldResolver.DEFAULT, + values); + } + } + else if (Types.getMinorTypeForArrowType(child.getType()) == Types.MinorType.VARCHAR) { + for (int i = 0; i < expectedRows; i++) { + List values = new ArrayList<>(); + values.add(String.valueOf(1000 + i)); + values.add(String.valueOf(1000 + i + 1)); + values.add(String.valueOf(1000 + i + 2)); + BlockUtils.setComplexValue((ListVector) vector, + i, + FieldResolver.DEFAULT, + values); + } + } + else if (Types.getMinorTypeForArrowType(child.getType()) == Types.MinorType.SMALLINT) { + for (int i = 0; i < expectedRows; i++) { + BlockUtils.setComplexValue((ListVector) vector, + i, + FieldResolver.DEFAULT, + Collections.singletonList((short) (i + 1))); + } + } + else if (Types.getMinorTypeForArrowType(child.getType()) == Types.MinorType.INT) { + for (int i = 0; i < expectedRows; i++) { + BlockUtils.setComplexValue((ListVector) vector, + i, + FieldResolver.DEFAULT, + Collections.singletonList(i)); + } + } + else if (Types.getMinorTypeForArrowType(child.getType()) == Types.MinorType.TINYINT) { + for (int i = 0; i < expectedRows; i++) { + BlockUtils.setComplexValue((ListVector) vector, + i, + FieldResolver.DEFAULT, + Collections.singletonList((byte) i)); + } + } + else if (Types.getMinorTypeForArrowType(child.getType()) == Types.MinorType.FLOAT4) { + for (int i = 0; i < expectedRows; i++) { + BlockUtils.setComplexValue((ListVector) vector, + i, + FieldResolver.DEFAULT, + Collections.singletonList((i * 1.0F))); + } + } + else if (Types.getMinorTypeForArrowType(child.getType()) == Types.MinorType.FLOAT8) { + for (int i = 0; i < expectedRows; i++) { + BlockUtils.setComplexValue((ListVector) vector, + i, + FieldResolver.DEFAULT, + Collections.singletonList((i * 1.0D))); + } + } + else if (Types.getMinorTypeForArrowType(child.getType()) == Types.MinorType.DECIMAL) { + for (int i = 0; i < expectedRows; i++) { + BlockUtils.setComplexValue((ListVector) vector, + i, + FieldResolver.DEFAULT, + Collections.singletonList((i * 1.0D))); + } + } + else if (Types.getMinorTypeForArrowType(child.getType()) == Types.MinorType.VARBINARY) { + for (int i = 0; i < expectedRows; i++) { + BlockUtils.setComplexValue((ListVector) vector, + i, + FieldResolver.DEFAULT, + Collections.singletonList(String.valueOf(i).getBytes(Charsets.UTF_8))); + } + } + else if (Types.getMinorTypeForArrowType(child.getType()) == Types.MinorType.BIT) { + for (int i = 0; i < expectedRows; i++) { + BlockUtils.setComplexValue((ListVector) vector, + i, + FieldResolver.DEFAULT, + Collections.singletonList(i % 2 == 1)); + } + } + break; + default: + throw new UnsupportedDataTypeException(vector.getMinorType() + " is not supported"); + } + fieldCount++; + } + expectedBlock.setRowCount(expectedRows); + + return expectedBlock; + } + + @Test + public void ListOfListsTest() + throws Exception + { + BlockAllocatorImpl expectedAllocator = new BlockAllocatorImpl(); + + /** + * Generate and write the schema + */ + SchemaBuilder schemaBuilder = new SchemaBuilder(); + schemaBuilder.addField( + FieldBuilder.newBuilder("outerlist", new ArrowType.List()) + .addListField("innerList", Types.MinorType.VARCHAR.getType()) + .build()); + Schema origSchema = schemaBuilder.build(); + + /** + * Generate and write the block + */ + Block expectedBlock = expectedAllocator.createBlock(origSchema); + + int expectedRows = 20; + for (Field next : origSchema.getFields()) { + ValueVector vector = expectedBlock.getFieldVector(next.getName()); + switch (vector.getMinorType()) { + case LIST: + Field child = vector.getField().getChildren().get(0); + for (int i = 0; i < expectedRows; i++) { + //For each row + List> value = new ArrayList<>(); + switch (Types.getMinorTypeForArrowType(child.getType())) { + case LIST: + List values = new ArrayList<>(); + values.add(String.valueOf(1000)); + values.add(String.valueOf(1001)); + values.add(String.valueOf(1002)); + value.add(values); + break; + default: + throw new UnsupportedDataTypeException(vector.getMinorType() + " is not supported"); + } + BlockUtils.setComplexValue((ListVector) vector, i, FieldResolver.DEFAULT, value); + } + break; + default: + throw new UnsupportedDataTypeException(vector.getMinorType() + " is not supported"); + } + } + expectedBlock.setRowCount(expectedRows); + + RecordBatchSerDe expectSerDe = new RecordBatchSerDe(expectedAllocator); + ByteArrayOutputStream blockOut = new ByteArrayOutputStream(); + ArrowRecordBatch expectedBatch = expectedBlock.getRecordBatch(); + expectSerDe.serialize(expectedBatch, blockOut); + expectedBatch.close(); + expectedBlock.close(); + + ByteArrayOutputStream schemaOut = new ByteArrayOutputStream(); + SchemaSerDe schemaSerDe = new SchemaSerDe(); + schemaSerDe.serialize(origSchema, schemaOut); + Schema actualSchema = schemaSerDe.deserialize(new ByteArrayInputStream(schemaOut.toByteArray())); + + BlockAllocatorImpl actualAllocator = new BlockAllocatorImpl(); + RecordBatchSerDe actualSerDe = new RecordBatchSerDe(actualAllocator); + ArrowRecordBatch batch = actualSerDe.deserialize(blockOut.toByteArray()); + + /** + * Generate and write the block + */ + Block actualBlock = actualAllocator.createBlock(actualSchema); + actualBlock.loadRecordBatch(batch); + batch.close(); + + for (int i = 0; i < actualBlock.getRowCount(); i++) { + logger.info("ListOfList: util {}", BlockUtils.rowToString(actualBlock, i)); + } + + assertEquals("Row count missmatch", expectedRows, actualBlock.getRowCount()); + int actualFieldCount = 1; + for (Field next : actualBlock.getFields()) { + FieldReader vector = actualBlock.getFieldReader(next.getName()); + switch (vector.getMinorType()) { + case LIST: + int actual = 0; + for (int i = 0; i < actualBlock.getRowCount(); i++) { + vector.setPosition(i); + int entryValues = 0; + while (vector.next()) { + FieldReader innerReader = vector.reader(); + int j = 0; + while (innerReader.next()) { + entryValues++; + assertEquals(String.valueOf(1000 + j++), innerReader.reader().readText().toString()); + } + } + if (entryValues > 0) {actual++;} + } + + assertEquals("failed for " + vector.getField().getName(), actualBlock.getRowCount(), actual); + break; + default: + throw new UnsupportedDataTypeException(next.getType().getTypeID() + " is not supported"); + } + actualFieldCount++; + } + + actualBlock.close(); + } + + @Test + public void ListOfStructsTest() + throws Exception + { + BlockAllocatorImpl expectedAllocator = new BlockAllocatorImpl(); + + /** + * Generate and write the schema + */ + SchemaBuilder schemaBuilder = new SchemaBuilder(); + schemaBuilder.addField( + FieldBuilder.newBuilder("outerlist", new ArrowType.List()) + .addField( + FieldBuilder.newBuilder("innerStruct", Types.MinorType.STRUCT.getType()) + .addStringField("varchar") + .addBigIntField("bigint") + .build()) + .build()); + Schema origSchema = schemaBuilder.build(); + + /** + * Generate and write the block + */ + Block expectedBlock = expectedAllocator.createBlock(origSchema); + + int expectedRows = 20; + for (Field next : origSchema.getFields()) { + ValueVector vector = expectedBlock.getFieldVector(next.getName()); + switch (vector.getMinorType()) { + case LIST: + Field child = vector.getField().getChildren().get(0); + for (int i = 0; i < expectedRows; i++) { + //For each row + List> value = new ArrayList<>(); + switch (Types.getMinorTypeForArrowType(child.getType())) { + case STRUCT: + Map values = new HashMap<>(); + values.put("varchar", "chars"); + values.put("bigint", 100L); + value.add(values); + break; + default: + throw new UnsupportedDataTypeException(vector.getMinorType() + " is not supported"); + } + BlockUtils.setComplexValue((ListVector) vector, i, FieldResolver.DEFAULT, value); + } + break; + default: + throw new UnsupportedDataTypeException(vector.getMinorType() + " is not supported"); + } + } + expectedBlock.setRowCount(expectedRows); + + RecordBatchSerDe expectSerDe = new RecordBatchSerDe(expectedAllocator); + ByteArrayOutputStream blockOut = new ByteArrayOutputStream(); + ArrowRecordBatch expectedBatch = expectedBlock.getRecordBatch(); + expectSerDe.serialize(expectedBatch, blockOut); + expectedBatch.close(); + expectedBlock.close(); + + ByteArrayOutputStream schemaOut = new ByteArrayOutputStream(); + SchemaSerDe schemaSerDe = new SchemaSerDe(); + schemaSerDe.serialize(origSchema, schemaOut); + Schema actualSchema = schemaSerDe.deserialize(new ByteArrayInputStream(schemaOut.toByteArray())); + + BlockAllocatorImpl actualAllocator = new BlockAllocatorImpl(); + RecordBatchSerDe actualSerDe = new RecordBatchSerDe(actualAllocator); + ArrowRecordBatch batch = actualSerDe.deserialize(blockOut.toByteArray()); + + /** + * Generate and write the block + */ + Block actualBlock = actualAllocator.createBlock(actualSchema); + actualBlock.loadRecordBatch(batch); + batch.close(); + + for (int i = 0; i < actualBlock.getRowCount(); i++) { + logger.info("ListOfList: util {}", BlockUtils.rowToString(actualBlock, i)); + } + + assertEquals("Row count missmatch", expectedRows, actualBlock.getRowCount()); + int actualFieldCount = 1; + for (Field next : actualBlock.getFields()) { + FieldReader vector = actualBlock.getFieldReader(next.getName()); + switch (vector.getMinorType()) { + case LIST: + int actual = 0; + for (int i = 0; i < actualBlock.getRowCount(); i++) { + vector.setPosition(i); + int entryValues = 0; + while (vector.next()) { + entryValues++; + assertEquals("chars", vector.reader().reader("varchar").readText().toString()); + assertEquals(Long.valueOf(100), vector.reader().reader("bigint").readLong()); + } + if (entryValues > 0) {actual++;} + } + + assertEquals("failed for " + vector.getField().getName(), actualBlock.getRowCount(), actual); + break; + default: + throw new UnsupportedDataTypeException(next.getType().getTypeID() + " is not supported"); + } + actualFieldCount++; + } + + actualBlock.close(); + } +} diff --git a/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/data/BlockUtilsTest.java b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/data/BlockUtilsTest.java new file mode 100644 index 0000000000..ad3da41974 --- /dev/null +++ b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/data/BlockUtilsTest.java @@ -0,0 +1,104 @@ +package com.amazonaws.athena.connector.lambda.data; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import org.apache.arrow.vector.complex.reader.FieldReader; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.arrow.vector.types.pojo.Schema; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + +import static com.amazonaws.athena.connector.lambda.data.BlockUtils.UTC_ZONE_ID; +import static org.junit.Assert.*; + +public class BlockUtilsTest +{ + private BlockAllocatorImpl allocator; + + @Before + public void setup() + { + allocator = new BlockAllocatorImpl(); + } + + @After + public void after() + { + allocator.close(); + } + + @Test + public void copyRows() + { + Schema schema = SchemaBuilder.newBuilder() + .addField("col1", new ArrowType.Int(32, true)) + .addField("col2", new ArrowType.Decimal(38, 9)) + .build(); + + //Make a block with 3 rows + Block src = allocator.createBlock(schema); + BlockUtils.setValue(src.getFieldVector("col1"), 0, 10); + BlockUtils.setValue(src.getFieldVector("col2"), 0, new BigDecimal(20)); + BlockUtils.setValue(src.getFieldVector("col1"), 1, 11); + BlockUtils.setValue(src.getFieldVector("col2"), 1, new BigDecimal(21)); + BlockUtils.setValue(src.getFieldVector("col1"), 2, 12); + BlockUtils.setValue(src.getFieldVector("col2"), 2, new BigDecimal(22)); + src.setRowCount(3); + + //Make the destination block + Block dst = allocator.createBlock(schema); + + assertEquals(3, BlockUtils.copyRows(src, dst, 0, 2)); + + assertEquals(src, dst); + } + + @Test + public void isNullRow() + { + Schema schema = SchemaBuilder.newBuilder() + .addField("col1", new ArrowType.Int(32, true)) + .addField("col2", new ArrowType.Int(32, true)) + .build(); + + //Make a block with 2 rows and no null rows + Block block = allocator.createBlock(schema); + BlockUtils.setValue(block.getFieldVector("col1"), 0, 10); + BlockUtils.setValue(block.getFieldVector("col2"), 0, 20); + BlockUtils.setValue(block.getFieldVector("col1"), 1, 11); + BlockUtils.setValue(block.getFieldVector("col2"), 1, 21); + block.setRowCount(2); + + assertFalse(BlockUtils.isNullRow(block, 1)); + + //now set a row to null + BlockUtils.unsetRow(1, block); + assertTrue(BlockUtils.isNullRow(block, 1)); + } +} diff --git a/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/data/S3BlockSpillerTest.java b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/data/S3BlockSpillerTest.java new file mode 100644 index 0000000000..69f9dd7b51 --- /dev/null +++ b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/data/S3BlockSpillerTest.java @@ -0,0 +1,207 @@ +package com.amazonaws.athena.connector.lambda.data; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.domain.predicate.ConstraintEvaluator; +import com.amazonaws.athena.connector.lambda.domain.spill.S3SpillLocation; +import com.amazonaws.athena.connector.lambda.domain.spill.SpillLocation; +import com.amazonaws.athena.connector.lambda.security.EncryptionKeyFactory; +import com.amazonaws.athena.connector.lambda.security.LocalKeyFactory; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.PutObjectResult; +import com.amazonaws.services.s3.model.S3Object; +import com.amazonaws.services.s3.model.S3ObjectInputStream; +import com.google.common.io.ByteStreams; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.arrow.vector.types.pojo.Schema; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.stubbing.Answer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class S3BlockSpillerTest +{ + private static final Logger logger = LoggerFactory.getLogger(S3BlockSpillerTest.class); + + private String bucket = "MyBucket"; + private String prefix = "blocks/spill"; + private String requestId = "requestId"; + private String splitId = "splitId"; + + @Mock + private AmazonS3 mockS3; + + private S3BlockSpiller blockWriter; + private EncryptionKeyFactory keyFactory = new LocalKeyFactory(); + private Block expected; + private BlockAllocatorImpl allocator; + private SpillConfig spillConfig; + + @Before + public void setup() + { + allocator = new BlockAllocatorImpl(); + + Schema schema = SchemaBuilder.newBuilder() + .addField("col1", new ArrowType.Int(32, true)) + .addField("col2", new ArrowType.Utf8()) + .build(); + + spillConfig = SpillConfig.newBuilder().withEncryptionKey(keyFactory.create()) + .withRequestId(requestId) + .withSpillLocation(S3SpillLocation.newBuilder() + .withBucket(bucket) + .withPrefix(prefix) + .withQueryId(requestId) + .withSplitId(splitId) + .withIsDirectory(true) + .build()) + .withRequestId(requestId) + .build(); + + blockWriter = new S3BlockSpiller(mockS3, spillConfig, allocator, schema, ConstraintEvaluator.emptyEvaluator()); + + expected = allocator.createBlock(schema); + BlockUtils.setValue(expected.getFieldVector("col1"), 1, 100); + BlockUtils.setValue(expected.getFieldVector("col2"), 1, "VarChar"); + BlockUtils.setValue(expected.getFieldVector("col1"), 1, 101); + BlockUtils.setValue(expected.getFieldVector("col2"), 1, "VarChar1"); + expected.setRowCount(2); + } + + @After + public void tearDown() + throws Exception + { + expected.close(); + allocator.close(); + blockWriter.close(); + } + + @Test + public void spillTest() + throws IOException + { + logger.info("spillTest: enter"); + + logger.info("spillTest: starting write test"); + + final ByteHolder byteHolder = new ByteHolder(); + + when(mockS3.putObject(eq(bucket), anyString(), anyObject(), anyObject())) + .thenAnswer(new Answer() + { + @Override + public Object answer(InvocationOnMock invocationOnMock) + throws Throwable + { + InputStream inputStream = (InputStream) invocationOnMock.getArguments()[2]; + byteHolder.setBytes(ByteStreams.toByteArray(inputStream)); + return mock(PutObjectResult.class); + } + }); + + SpillLocation blockLocation = blockWriter.write(expected); + + if (blockLocation instanceof S3SpillLocation) { + assertEquals(bucket, ((S3SpillLocation) blockLocation).getBucket()); + assertEquals(prefix + "/" + requestId + "/" + splitId + ".0", ((S3SpillLocation) blockLocation).getKey()); + } + + SpillLocation blockLocation2 = blockWriter.write(expected); + + if (blockLocation2 instanceof S3SpillLocation) { + assertEquals(bucket, ((S3SpillLocation) blockLocation2).getBucket()); + assertEquals(prefix + "/" + requestId + "/" + splitId + ".1", ((S3SpillLocation) blockLocation2).getKey()); + } + + verify(mockS3, times(1)) + .putObject(eq(bucket), eq(prefix + "/" + requestId + "/" + splitId + ".0"), anyObject(), anyObject()); + verify(mockS3, times(1)) + .putObject(eq(bucket), eq(prefix + "/" + requestId + "/" + splitId + ".1"), anyObject(), anyObject()); + + verifyNoMoreInteractions(mockS3); + reset(mockS3); + + logger.info("spillTest: Starting read test."); + + when(mockS3.getObject(eq(bucket), eq(prefix + "/" + requestId + "/" + splitId + ".1"))) + .thenAnswer(new Answer() + { + @Override + public Object answer(InvocationOnMock invocationOnMock) + throws Throwable + { + S3Object mockObject = mock(S3Object.class); + when(mockObject.getObjectContent()).thenReturn(new S3ObjectInputStream(new ByteArrayInputStream(byteHolder.getBytes()), null)); + return mockObject; + } + }); + + Block block = blockWriter.read((S3SpillLocation) blockLocation2, spillConfig.getEncryptionKey(), expected.getSchema()); + + assertEquals(expected, block); + + verify(mockS3, times(1)) + .getObject(eq(bucket), eq(prefix + "/" + requestId + "/" + splitId + ".1")); + + verifyNoMoreInteractions(mockS3); + + logger.info("spillTest: exit"); + } + + private class ByteHolder + { + private byte[] bytes; + + public void setBytes(byte[] bytes) + { + this.bytes = bytes; + } + + public byte[] getBytes() + { + return bytes; + } + } +} diff --git a/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/data/UnitTestBlockUtils.java b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/data/UnitTestBlockUtils.java new file mode 100644 index 0000000000..2707344116 --- /dev/null +++ b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/data/UnitTestBlockUtils.java @@ -0,0 +1,147 @@ +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connector.lambda.data; + +import org.apache.arrow.vector.complex.reader.FieldReader; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.arrow.vector.util.Text; + +import java.time.Instant; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static com.amazonaws.athena.connector.lambda.data.BlockUtils.UTC_ZONE_ID; + +public class UnitTestBlockUtils +{ + + /** + * Used to get values (Integer, Double, String, etc) from the Arrow values in the fieldReader + * @param fieldReader the field reader containing the arrow value + * @return the value object in java type + */ + public static Object getValue(FieldReader fieldReader, int pos) + { + fieldReader.setPosition(pos); + + Types.MinorType minorType = fieldReader.getMinorType(); + switch (minorType) { + case DATEMILLI: + if (Objects.isNull(fieldReader.readLocalDateTime())) { + return null; + } + long millis = fieldReader.readLocalDateTime().toDateTime(org.joda.time.DateTimeZone.UTC).getMillis(); + return Instant.ofEpochMilli(millis).atZone(UTC_ZONE_ID).toLocalDateTime(); + case TINYINT: + case UINT1: + return fieldReader.readByte(); + case UINT2: + return fieldReader.readCharacter(); + case SMALLINT: + return fieldReader.readShort(); + case DATEDAY: + Integer intVal = fieldReader.readInteger(); + if (Objects.isNull(intVal)) { + return null; + } + return LocalDate.ofEpochDay(intVal); + case INT: + case UINT4: + return fieldReader.readInteger(); + case UINT8: + case BIGINT: + return fieldReader.readLong(); + case DECIMAL: + return fieldReader.readBigDecimal(); + case FLOAT4: + return fieldReader.readFloat(); + case FLOAT8: + return fieldReader.readDouble(); + case VARCHAR: + Text text = fieldReader.readText(); + if (Objects.isNull(text)) { + return null; + } + return text.toString(); + case VARBINARY: + return fieldReader.readByteArray(); + case BIT: + return fieldReader.readBoolean(); + case LIST: + return readList(fieldReader); + case STRUCT: + return readStruct(fieldReader); + default: + throw new IllegalArgumentException("Unsupported type " + minorType); + } + } + + /** + * Recursively read the values of a complex list + * @param listReader + * @return + */ + private static List readList(FieldReader listReader) + { + if (!listReader.isSet()) { + return null; + } + + List list = new ArrayList<>(); + + while (listReader.next()) { + FieldReader subReader = listReader.reader(); + if (!subReader.isSet()) { + list.add(null); + continue; + } + list.add(getValue(subReader, subReader.getPosition())); + } + return list; + } + + /** + * Recursively reads the value of a complex struct object. + * @param structReader + * @return + */ + private static Map readStruct(FieldReader structReader) + { + if (!structReader.isSet()) { + return null; + } + + List fields = structReader.getField().getChildren(); + + Map nameToValues = new HashMap<>(); + for (Field child : fields) { + FieldReader subReader = structReader.reader(child.getName()); + nameToValues.put(child.getName(), getValue(subReader, subReader.getPosition())); + } + + return nameToValues; + } + +} diff --git a/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/domain/predicate/AllOrNoneValueSetTest.java b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/domain/predicate/AllOrNoneValueSetTest.java new file mode 100644 index 0000000000..e5897769f6 --- /dev/null +++ b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/domain/predicate/AllOrNoneValueSetTest.java @@ -0,0 +1,177 @@ +package com.amazonaws.athena.connector.lambda.domain.predicate; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import org.apache.arrow.vector.types.Types; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.util.Collections; + +import static org.junit.Assert.*; + +public class AllOrNoneValueSetTest +{ + private BlockAllocatorImpl allocator; + + @Before + public void setup() + { + allocator = new BlockAllocatorImpl(); + } + + @After + public void tearDown() + { + allocator.close(); + } + + @Test + public void testAll() + throws Exception + { + AllOrNoneValueSet valueSet = AllOrNoneValueSet.all(Types.MinorType.INT.getType()); + assertEquals(valueSet.getType(), Types.MinorType.INT.getType()); + assertFalse(valueSet.isNone()); + assertTrue(valueSet.isAll()); + assertFalse(valueSet.isSingleValue()); + assertTrue(valueSet.containsValue(Marker.exactly(allocator, Types.MinorType.INT.getType(), 0))); + + try { + valueSet.getSingleValue(); + fail(); + } + catch (Exception ignored) { + } + } + + @Test + public void testNullability() + throws Exception + { + ValueSet notNull = AllOrNoneValueSet.notNull(Types.MinorType.INT.getType()); + assertTrue(notNull.containsValue(Marker.exactly(allocator, Types.MinorType.INT.getType(), 100))); + assertFalse(notNull.containsValue(Marker.nullMarker(allocator, Types.MinorType.INT.getType()))); + assertTrue(notNull.containsValue(Marker.exactly(allocator, Types.MinorType.INT.getType(), 101))); + + ValueSet onlyNull = AllOrNoneValueSet.onlyNull(Types.MinorType.INT.getType()); + assertFalse(onlyNull.containsValue(Marker.exactly(allocator, Types.MinorType.INT.getType(), 100))); + assertTrue(onlyNull.containsValue(Marker.nullMarker(allocator, Types.MinorType.INT.getType()))); + assertFalse(onlyNull.containsValue(Marker.exactly(allocator, Types.MinorType.INT.getType(), 101))); + } + + @Test + public void testNone() + throws Exception + { + AllOrNoneValueSet valueSet = AllOrNoneValueSet.none(Types.MinorType.INT.getType()); + assertEquals(valueSet.getType(), Types.MinorType.INT.getType()); + assertTrue(valueSet.isNone()); + assertFalse(valueSet.isAll()); + assertFalse(valueSet.isSingleValue()); + assertFalse(valueSet.containsValue(Marker.exactly(allocator, Types.MinorType.INT.getType(), 0))); + + try { + valueSet.getSingleValue(); + fail(); + } + catch (Exception ignored) { + } + } + + @Test + public void testIntersect() + throws Exception + { + AllOrNoneValueSet all = AllOrNoneValueSet.all(Types.MinorType.INT.getType()); + AllOrNoneValueSet none = AllOrNoneValueSet.none(Types.MinorType.INT.getType()); + + assertEquals(all.intersect(allocator, all), all); + assertEquals(all.intersect(allocator, none), none); + assertEquals(none.intersect(allocator, all), none); + assertEquals(none.intersect(allocator, none), none); + } + + @Test + public void testUnion() + throws Exception + { + AllOrNoneValueSet all = AllOrNoneValueSet.all(Types.MinorType.INT.getType()); + AllOrNoneValueSet none = AllOrNoneValueSet.none(Types.MinorType.INT.getType()); + + assertEquals(all.union(allocator, all), all); + assertEquals(all.union(allocator, none), all); + assertEquals(none.union(allocator, all), all); + assertEquals(none.union(allocator, none), none); + } + + @Test + public void testComplement() + throws Exception + { + AllOrNoneValueSet all = AllOrNoneValueSet.all(Types.MinorType.INT.getType()); + AllOrNoneValueSet none = AllOrNoneValueSet.none(Types.MinorType.INT.getType()); + + assertEquals(all.complement(allocator), none); + assertEquals(none.complement(allocator), all); + } + + @Test + public void testOverlaps() + throws Exception + { + AllOrNoneValueSet all = AllOrNoneValueSet.all(Types.MinorType.INT.getType()); + AllOrNoneValueSet none = AllOrNoneValueSet.none(Types.MinorType.INT.getType()); + + assertTrue(all.overlaps(allocator, all)); + assertFalse(all.overlaps(allocator, none)); + assertFalse(none.overlaps(allocator, all)); + assertFalse(none.overlaps(allocator, none)); + } + + @Test + public void testSubtract() + throws Exception + { + AllOrNoneValueSet all = AllOrNoneValueSet.all(Types.MinorType.INT.getType()); + AllOrNoneValueSet none = AllOrNoneValueSet.none(Types.MinorType.INT.getType()); + + assertEquals(all.subtract(allocator, all), none); + assertEquals(all.subtract(allocator, none), all); + assertEquals(none.subtract(allocator, all), none); + assertEquals(none.subtract(allocator, none), none); + } + + @Test + public void testContains() + throws Exception + { + AllOrNoneValueSet all = AllOrNoneValueSet.all(Types.MinorType.INT.getType()); + AllOrNoneValueSet none = AllOrNoneValueSet.none(Types.MinorType.INT.getType()); + + assertTrue(all.contains(allocator, all)); + assertTrue(all.contains(allocator, none)); + assertFalse(none.contains(allocator, all)); + assertTrue(none.contains(allocator, none)); + } +} diff --git a/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/domain/predicate/EquatableValueSetTest.java b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/domain/predicate/EquatableValueSetTest.java new file mode 100644 index 0000000000..fd3fa81c26 --- /dev/null +++ b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/domain/predicate/EquatableValueSetTest.java @@ -0,0 +1,328 @@ +package com.amazonaws.athena.connector.lambda.domain.predicate; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.util.Collections; + +import static org.junit.Assert.*; + +public class EquatableValueSetTest +{ + private BlockAllocatorImpl allocator; + + @Before + public void setup() + { + allocator = new BlockAllocatorImpl(); + } + + @After + public void tearDown() + { + allocator.close(); + } + + private ArrowType INT = Types.MinorType.INT.getType(); + + @Test + public void testEmptySet() + throws Exception + { + EquatableValueSet equatables = EquatableValueSet.none(allocator, INT); + assertEquals(equatables.getType(), INT); + assertTrue(equatables.isNone()); + assertFalse(equatables.isAll()); + assertFalse(equatables.isSingleValue()); + assertTrue(equatables.isWhiteList()); + assertEquals(equatables.getValues().getRowCount(), 0); + assertEquals(equatables.complement(allocator), EquatableValueSet.all(allocator, INT)); + assertFalse(equatables.containsValue(0)); + assertFalse(equatables.containsValue(1)); + } + + @Test + public void testEntireSet() + throws Exception + { + EquatableValueSet equatables = EquatableValueSet.all(allocator, INT); + assertEquals(equatables.getType(), INT); + assertFalse(equatables.isNone()); + assertTrue(equatables.isAll()); + assertFalse(equatables.isSingleValue()); + assertFalse(equatables.isWhiteList()); + assertEquals(equatables.getValues().getRowCount(), 0); + assertEquals(equatables.complement(allocator), EquatableValueSet.none(allocator, INT)); + assertTrue(equatables.containsValue(0)); + assertTrue(equatables.containsValue(1)); + } + + @Test + public void testSingleValue() + throws Exception + { + EquatableValueSet equatables = EquatableValueSet.of(allocator, INT, 10); + + EquatableValueSet complement = (EquatableValueSet) EquatableValueSet.all(allocator, INT).subtract(allocator, equatables); + + // Whitelist + assertEquals(equatables.getType(), INT); + assertFalse(equatables.isNone()); + assertFalse(equatables.isAll()); + assertTrue(equatables.isSingleValue()); + assertTrue(equatables.isWhiteList()); + assertEquals(equatables.getSingleValue(), 10); + assertEquals(equatables.complement(allocator), complement); + assertFalse(equatables.containsValue(0)); + assertFalse(equatables.containsValue(1)); + assertTrue(equatables.containsValue(10)); + + // Blacklist + assertEquals(complement.getType(), INT); + assertFalse(complement.isNone()); + assertFalse(complement.isAll()); + assertFalse(complement.isSingleValue()); + assertFalse(complement.isWhiteList()); + assertEquals(complement.toString(), complement.getValue(0), 10); + assertEquals(complement.complement(allocator), equatables); + assertTrue(complement.containsValue(0)); + assertTrue(complement.containsValue(1)); + assertFalse(complement.containsValue(10)); + } + + @Test + public void testMultipleValues() + throws Exception + { + EquatableValueSet equatables = EquatableValueSet.of(allocator, INT, 1, 2, 3, 1); + + EquatableValueSet complement = (EquatableValueSet) EquatableValueSet.all(allocator, INT).subtract(allocator, equatables); + + // Whitelist + assertEquals(equatables.getType(), INT); + assertFalse(equatables.isNone()); + assertFalse(equatables.isAll()); + assertFalse(equatables.isSingleValue()); + assertTrue(equatables.isWhiteList()); + assertEquals(equatables.complement(allocator), complement); + assertFalse(equatables.containsValue(0)); + assertTrue(equatables.containsValue(1)); + assertTrue(equatables.containsValue(2)); + assertTrue(equatables.containsValue(3)); + assertFalse(equatables.containsValue(4)); + + // Blacklist + assertEquals(complement.getType(), INT); + assertFalse(complement.isNone()); + assertFalse(complement.isAll()); + assertFalse(complement.isSingleValue()); + assertFalse(complement.isWhiteList()); + assertEquals(complement.complement(allocator), equatables); + assertTrue(complement.containsValue(0)); + assertFalse(complement.containsValue(1)); + assertFalse(complement.containsValue(2)); + assertFalse(complement.containsValue(3)); + assertTrue(complement.containsValue(4)); + } + + @Test + public void testGetSingleValue() + throws Exception + { + assertEquals(EquatableValueSet.of(allocator, INT, 0).getSingleValue(), 0); + try { + EquatableValueSet.all(allocator, INT).getSingleValue(); + fail(); + } + catch (IllegalStateException ignored) { + } + } + + @Test + public void testNullability() + throws Exception + { + ValueSet actual = EquatableValueSet.of(allocator, INT, true, Collections.singletonList(100)); + assertTrue(actual.containsValue(Marker.exactly(allocator, INT, 100))); + assertTrue(actual.containsValue(Marker.nullMarker(allocator, INT))); + assertFalse(actual.containsValue(Marker.exactly(allocator, INT, 101))); + } + + @Test + public void testOverlaps() + throws Exception + { + assertTrue(EquatableValueSet.all(allocator, INT).overlaps(allocator, EquatableValueSet.all(allocator, INT))); + assertFalse(EquatableValueSet.all(allocator, INT).overlaps(allocator, EquatableValueSet.none(allocator, INT))); + assertTrue(EquatableValueSet.all(allocator, INT).overlaps(allocator, EquatableValueSet.of(allocator, INT, 0))); + assertTrue(EquatableValueSet.all(allocator, INT).overlaps(allocator, EquatableValueSet.of(allocator, INT, 0, 1))); + assertTrue(EquatableValueSet.all(allocator, INT).overlaps(allocator, EquatableValueSet.of(allocator, INT, 0, 1).complement(allocator))); + + assertFalse(EquatableValueSet.none(allocator, INT).overlaps(allocator, EquatableValueSet.all(allocator, INT))); + assertFalse(EquatableValueSet.none(allocator, INT).overlaps(allocator, EquatableValueSet.none(allocator, INT))); + assertFalse(EquatableValueSet.none(allocator, INT).overlaps(allocator, EquatableValueSet.of(allocator, INT, 0))); + assertFalse(EquatableValueSet.none(allocator, INT).overlaps(allocator, EquatableValueSet.of(allocator, INT, 0, 1))); + assertFalse(EquatableValueSet.none(allocator, INT).overlaps(allocator, EquatableValueSet.of(allocator, INT, 0, 1).complement(allocator))); + + assertTrue(EquatableValueSet.of(allocator, INT, 0).overlaps(allocator, EquatableValueSet.all(allocator, INT))); + assertFalse(EquatableValueSet.of(allocator, INT, 0).overlaps(allocator, EquatableValueSet.none(allocator, INT))); + assertTrue(EquatableValueSet.of(allocator, INT, 0).overlaps(allocator, EquatableValueSet.of(allocator, INT, 0))); + assertFalse(EquatableValueSet.of(allocator, INT, 0).overlaps(allocator, EquatableValueSet.of(allocator, INT, 1))); + assertTrue(EquatableValueSet.of(allocator, INT, 0).overlaps(allocator, EquatableValueSet.of(allocator, INT, 0, 1))); + assertFalse(EquatableValueSet.of(allocator, INT, 0).overlaps(allocator, EquatableValueSet.of(allocator, INT, 0, 1).complement(allocator))); + assertFalse(EquatableValueSet.of(allocator, INT, 0).overlaps(allocator, EquatableValueSet.of(allocator, INT, 0).complement(allocator))); + assertTrue(EquatableValueSet.of(allocator, INT, 0).overlaps(allocator, EquatableValueSet.of(allocator, INT, 1).complement(allocator))); + + assertTrue(EquatableValueSet.of(allocator, INT, 0, 1).overlaps(allocator, EquatableValueSet.all(allocator, INT))); + assertFalse(EquatableValueSet.of(allocator, INT, 0, 1).overlaps(allocator, EquatableValueSet.none(allocator, INT))); + assertTrue(EquatableValueSet.of(allocator, INT, 0, 1).overlaps(allocator, EquatableValueSet.of(allocator, INT, 0))); + assertFalse(EquatableValueSet.of(allocator, INT, 0, 1).overlaps(allocator, EquatableValueSet.of(allocator, INT, -1))); + assertTrue(EquatableValueSet.of(allocator, INT, 0, 1).overlaps(allocator, EquatableValueSet.of(allocator, INT, 0, 1))); + assertTrue(EquatableValueSet.of(allocator, INT, 0, 1).overlaps(allocator, EquatableValueSet.of(allocator, INT, -1).complement(allocator))); + + assertTrue(EquatableValueSet.of(allocator, INT, 0, 1).complement(allocator).overlaps(allocator, EquatableValueSet.all(allocator, INT))); + assertFalse(EquatableValueSet.of(allocator, INT, 0, 1).complement(allocator).overlaps(allocator, EquatableValueSet.none(allocator, INT))); + assertFalse(EquatableValueSet.of(allocator, INT, 0, 1).complement(allocator).overlaps(allocator, EquatableValueSet.of(allocator, INT, 0))); + assertTrue(EquatableValueSet.of(allocator, INT, 0, 1).complement(allocator).overlaps(allocator, EquatableValueSet.of(allocator, INT, -1))); + assertFalse(EquatableValueSet.of(allocator, INT, 0, 1).complement(allocator).overlaps(allocator, EquatableValueSet.of(allocator, INT, 0, 1))); + assertTrue(EquatableValueSet.of(allocator, INT, 0, 1).complement(allocator).overlaps(allocator, EquatableValueSet.of(allocator, INT, -1).complement(allocator))); + } + + @Test + public void testContains() + throws Exception + { + assertTrue(EquatableValueSet.all(allocator, INT).contains(allocator, EquatableValueSet.all(allocator, INT))); + assertTrue(EquatableValueSet.all(allocator, INT).contains(allocator, EquatableValueSet.none(allocator, INT))); + assertTrue(EquatableValueSet.all(allocator, INT).contains(allocator, EquatableValueSet.of(allocator, INT, 0))); + assertTrue(EquatableValueSet.all(allocator, INT).contains(allocator, EquatableValueSet.of(allocator, INT, 0, 1))); + assertTrue(EquatableValueSet.all(allocator, INT).contains(allocator, EquatableValueSet.of(allocator, INT, 0, 1).complement(allocator))); + + assertFalse(EquatableValueSet.none(allocator, INT).contains(allocator, EquatableValueSet.all(allocator, INT))); + assertTrue(EquatableValueSet.none(allocator, INT).contains(allocator, EquatableValueSet.none(allocator, INT))); + assertFalse(EquatableValueSet.none(allocator, INT).contains(allocator, EquatableValueSet.of(allocator, INT, 0))); + assertFalse(EquatableValueSet.none(allocator, INT).contains(allocator, EquatableValueSet.of(allocator, INT, 0, 1))); + assertFalse(EquatableValueSet.none(allocator, INT).contains(allocator, EquatableValueSet.of(allocator, INT, 0, 1).complement(allocator))); + + assertFalse(EquatableValueSet.of(allocator, INT, 0).contains(allocator, EquatableValueSet.all(allocator, INT))); + assertTrue(EquatableValueSet.of(allocator, INT, 0).contains(allocator, EquatableValueSet.none(allocator, INT))); + assertTrue(EquatableValueSet.of(allocator, INT, 0).contains(allocator, EquatableValueSet.of(allocator, INT, 0))); + assertFalse(EquatableValueSet.of(allocator, INT, 0).contains(allocator, EquatableValueSet.of(allocator, INT, 0, 1))); + assertFalse(EquatableValueSet.of(allocator, INT, 0).contains(allocator, EquatableValueSet.of(allocator, INT, 0, 1).complement(allocator))); + assertFalse(EquatableValueSet.of(allocator, INT, 0).contains(allocator, EquatableValueSet.of(allocator, INT, 0).complement(allocator))); + assertFalse(EquatableValueSet.of(allocator, INT, 0).contains(allocator, EquatableValueSet.of(allocator, INT, 1).complement(allocator))); + + assertFalse(EquatableValueSet.of(allocator, INT, 0, 1).contains(allocator, EquatableValueSet.all(allocator, INT))); + assertTrue(EquatableValueSet.of(allocator, INT, 0, 1).contains(allocator, EquatableValueSet.none(allocator, INT))); + assertTrue(EquatableValueSet.of(allocator, INT, 0, 1).contains(allocator, EquatableValueSet.of(allocator, INT, 0))); + assertTrue(EquatableValueSet.of(allocator, INT, 0, 1).contains(allocator, EquatableValueSet.of(allocator, INT, 0, 1))); + assertFalse(EquatableValueSet.of(allocator, INT, 0, 1).contains(allocator, EquatableValueSet.of(allocator, INT, 0, 2))); + assertFalse(EquatableValueSet.of(allocator, INT, 0, 1).contains(allocator, EquatableValueSet.of(allocator, INT, 0, 1).complement(allocator))); + assertFalse(EquatableValueSet.of(allocator, INT, 0, 1).contains(allocator, EquatableValueSet.of(allocator, INT, 0).complement(allocator))); + assertFalse(EquatableValueSet.of(allocator, INT, 0, 1).contains(allocator, EquatableValueSet.of(allocator, INT, 1).complement(allocator))); + + assertFalse(EquatableValueSet.of(allocator, INT, 0, 1).complement(allocator).contains(allocator, EquatableValueSet.all(allocator, INT))); + assertTrue(EquatableValueSet.of(allocator, INT, 0, 1).complement(allocator).contains(allocator, EquatableValueSet.none(allocator, INT))); + assertFalse(EquatableValueSet.of(allocator, INT, 0, 1).complement(allocator).contains(allocator, EquatableValueSet.of(allocator, INT, 0))); + assertTrue(EquatableValueSet.of(allocator, INT, 0, 1).complement(allocator).contains(allocator, EquatableValueSet.of(allocator, INT, -1))); + assertFalse(EquatableValueSet.of(allocator, INT, 0, 1).complement(allocator).contains(allocator, EquatableValueSet.of(allocator, INT, 0, 1))); + assertFalse(EquatableValueSet.of(allocator, INT, 0, 1).complement(allocator).contains(allocator, EquatableValueSet.of(allocator, INT, -1).complement(allocator))); + } + + @Test + public void testIntersect() + throws Exception + { + assertEquals(EquatableValueSet.none(allocator, INT).intersect(allocator, EquatableValueSet.none(allocator, INT)), EquatableValueSet.none(allocator, INT)); + assertEquals(EquatableValueSet.all(allocator, INT).intersect(allocator, EquatableValueSet.all(allocator, INT)), EquatableValueSet.all(allocator, INT)); + assertEquals(EquatableValueSet.none(allocator, INT).intersect(allocator, EquatableValueSet.all(allocator, INT)), EquatableValueSet.none(allocator, INT)); + assertEquals(EquatableValueSet.none(allocator, INT).intersect(allocator, EquatableValueSet.of(allocator, INT, 0)), EquatableValueSet.none(allocator, INT)); + assertEquals(EquatableValueSet.all(allocator, INT).intersect(allocator, EquatableValueSet.of(allocator, INT, 0)), EquatableValueSet.of(allocator, INT, 0)); + assertEquals(EquatableValueSet.of(allocator, INT, 0).intersect(allocator, EquatableValueSet.of(allocator, INT, 0)), EquatableValueSet.of(allocator, INT, 0)); + assertEquals(EquatableValueSet.of(allocator, INT, 0, 1).intersect(allocator, EquatableValueSet.of(allocator, INT, 0)), EquatableValueSet.of(allocator, INT, 0)); + assertEquals(EquatableValueSet.of(allocator, INT, 0).complement(allocator).intersect(allocator, EquatableValueSet.of(allocator, INT, 0)), EquatableValueSet.none(allocator, INT)); + assertEquals(EquatableValueSet.of(allocator, INT, 0).complement(allocator).intersect(allocator, EquatableValueSet.of(allocator, INT, 1)), EquatableValueSet.of(allocator, INT, 1)); + assertEquals(EquatableValueSet.of(allocator, INT, 0).intersect(allocator, EquatableValueSet.of(allocator, INT, 1).complement(allocator)), EquatableValueSet.of(allocator, INT, 0)); + assertEquals(EquatableValueSet.of(allocator, INT, 0, 1).intersect(allocator, EquatableValueSet.of(allocator, INT, 0, 2)), EquatableValueSet.of(allocator, INT, 0)); + assertEquals(EquatableValueSet.of(allocator, INT, 0, 1).complement(allocator).intersect(allocator, EquatableValueSet.of(allocator, INT, 0, 2)), EquatableValueSet.of(allocator, INT, 2)); + assertEquals(EquatableValueSet.of(allocator, INT, 0, 1).complement(allocator).intersect(allocator, EquatableValueSet.of(allocator, INT, 0, 2).complement(allocator)), EquatableValueSet.of(allocator, INT, 0, 1, 2).complement(allocator)); + } + + @Test + public void testUnion() + throws Exception + { + assertEquals(EquatableValueSet.none(allocator, INT).union(allocator, EquatableValueSet.none(allocator, INT)), EquatableValueSet.none(allocator, INT)); + assertEquals(EquatableValueSet.all(allocator, INT).union(allocator, EquatableValueSet.all(allocator, INT)), EquatableValueSet.all(allocator, INT)); + assertEquals(EquatableValueSet.none(allocator, INT).union(allocator, EquatableValueSet.all(allocator, INT)), EquatableValueSet.all(allocator, INT)); + assertEquals(EquatableValueSet.none(allocator, INT).union(allocator, EquatableValueSet.of(allocator, INT, 0)), EquatableValueSet.of(allocator, INT, 0)); + assertEquals(EquatableValueSet.all(allocator, INT).union(allocator, EquatableValueSet.of(allocator, INT, 0)), EquatableValueSet.all(allocator, INT)); + assertEquals(EquatableValueSet.of(allocator, INT, 0).union(allocator, EquatableValueSet.of(allocator, INT, 0)), EquatableValueSet.of(allocator, INT, 0)); + assertEquals(EquatableValueSet.of(allocator, INT, 0, 1).union(allocator, EquatableValueSet.of(allocator, INT, 0)), EquatableValueSet.of(allocator, INT, 0, 1)); + assertEquals(EquatableValueSet.of(allocator, INT, 0).complement(allocator).union(allocator, EquatableValueSet.of(allocator, INT, 0)), EquatableValueSet.all(allocator, INT)); + assertEquals(EquatableValueSet.of(allocator, INT, 0).complement(allocator).union(allocator, EquatableValueSet.of(allocator, INT, 1)), EquatableValueSet.of(allocator, INT, 0).complement(allocator)); + assertEquals(EquatableValueSet.of(allocator, INT, 0).union(allocator, EquatableValueSet.of(allocator, INT, 1).complement(allocator)), EquatableValueSet.of(allocator, INT, 1).complement(allocator)); + assertEquals(EquatableValueSet.of(allocator, INT, 0, 1).union(allocator, EquatableValueSet.of(allocator, INT, 0, 2)), EquatableValueSet.of(allocator, INT, 0, 1, 2)); + assertEquals(EquatableValueSet.of(allocator, INT, 0, 1).complement(allocator).union(allocator, EquatableValueSet.of(allocator, INT, 0, 2)), EquatableValueSet.of(allocator, INT, 1).complement(allocator)); + assertEquals(EquatableValueSet.of(allocator, INT, 0, 1).complement(allocator).union(allocator, EquatableValueSet.of(allocator, INT, 0, 2).complement(allocator)), EquatableValueSet.of(allocator, INT, 0).complement(allocator)); + } + + @Test + public void testSubtract() + throws Exception + { + assertEquals(EquatableValueSet.all(allocator, INT).subtract(allocator, EquatableValueSet.all(allocator, INT)), EquatableValueSet.none(allocator, INT)); + assertEquals(EquatableValueSet.all(allocator, INT).subtract(allocator, EquatableValueSet.none(allocator, INT)), EquatableValueSet.all(allocator, INT)); + assertEquals(EquatableValueSet.all(allocator, INT).subtract(allocator, EquatableValueSet.of(allocator, INT, 0)), EquatableValueSet.of(allocator, INT, 0).complement(allocator)); + assertEquals(EquatableValueSet.all(allocator, INT).subtract(allocator, EquatableValueSet.of(allocator, INT, 0, 1)), EquatableValueSet.of(allocator, INT, 0, 1).complement(allocator)); + assertEquals(EquatableValueSet.all(allocator, INT).subtract(allocator, EquatableValueSet.of(allocator, INT, 0, 1).complement(allocator)), EquatableValueSet.of(allocator, INT, 0, 1)); + + assertEquals(EquatableValueSet.none(allocator, INT).subtract(allocator, EquatableValueSet.all(allocator, INT)), EquatableValueSet.none(allocator, INT)); + assertEquals(EquatableValueSet.none(allocator, INT).subtract(allocator, EquatableValueSet.none(allocator, INT)), EquatableValueSet.none(allocator, INT)); + assertEquals(EquatableValueSet.none(allocator, INT).subtract(allocator, EquatableValueSet.of(allocator, INT, 0)), EquatableValueSet.none(allocator, INT)); + assertEquals(EquatableValueSet.none(allocator, INT).subtract(allocator, EquatableValueSet.of(allocator, INT, 0, 1)), EquatableValueSet.none(allocator, INT)); + assertEquals(EquatableValueSet.none(allocator, INT).subtract(allocator, EquatableValueSet.of(allocator, INT, 0, 1).complement(allocator)), EquatableValueSet.none(allocator, INT)); + + assertEquals(EquatableValueSet.of(allocator, INT, 0).subtract(allocator, EquatableValueSet.all(allocator, INT)), EquatableValueSet.none(allocator, INT)); + assertEquals(EquatableValueSet.of(allocator, INT, 0).subtract(allocator, EquatableValueSet.none(allocator, INT)), EquatableValueSet.of(allocator, INT, 0)); + assertEquals(EquatableValueSet.of(allocator, INT, 0).subtract(allocator, EquatableValueSet.of(allocator, INT, 0)), EquatableValueSet.none(allocator, INT)); + assertEquals(EquatableValueSet.of(allocator, INT, 0).subtract(allocator, EquatableValueSet.of(allocator, INT, 0).complement(allocator)), EquatableValueSet.of(allocator, INT, 0)); + assertEquals(EquatableValueSet.of(allocator, INT, 0).subtract(allocator, EquatableValueSet.of(allocator, INT, 1)), EquatableValueSet.of(allocator, INT, 0)); + assertEquals(EquatableValueSet.of(allocator, INT, 0).subtract(allocator, EquatableValueSet.of(allocator, INT, 1).complement(allocator)), EquatableValueSet.none(allocator, INT)); + assertEquals(EquatableValueSet.of(allocator, INT, 0).subtract(allocator, EquatableValueSet.of(allocator, INT, 0, 1)), EquatableValueSet.none(allocator, INT)); + assertEquals(EquatableValueSet.of(allocator, INT, 0).subtract(allocator, EquatableValueSet.of(allocator, INT, 0, 1).complement(allocator)), EquatableValueSet.of(allocator, INT, 0)); + + assertEquals(EquatableValueSet.of(allocator, INT, 0).complement(allocator).subtract(allocator, EquatableValueSet.all(allocator, INT)), EquatableValueSet.none(allocator, INT)); + assertEquals(EquatableValueSet.of(allocator, INT, 0).complement(allocator).subtract(allocator, EquatableValueSet.none(allocator, INT)), EquatableValueSet.of(allocator, INT, 0).complement(allocator)); + assertEquals(EquatableValueSet.of(allocator, INT, 0).complement(allocator).subtract(allocator, EquatableValueSet.of(allocator, INT, 0)), EquatableValueSet.of(allocator, INT, 0).complement(allocator)); + assertEquals(EquatableValueSet.of(allocator, INT, 0).complement(allocator).subtract(allocator, EquatableValueSet.of(allocator, INT, 0).complement(allocator)), EquatableValueSet.none(allocator, INT)); + assertEquals(EquatableValueSet.of(allocator, INT, 0).complement(allocator).subtract(allocator, EquatableValueSet.of(allocator, INT, 1)), EquatableValueSet.of(allocator, INT, 0, 1).complement(allocator)); + assertEquals(EquatableValueSet.of(allocator, INT, 0).complement(allocator).subtract(allocator, EquatableValueSet.of(allocator, INT, 1).complement(allocator)), EquatableValueSet.of(allocator, INT, 1)); + assertEquals(EquatableValueSet.of(allocator, INT, 0).complement(allocator).subtract(allocator, EquatableValueSet.of(allocator, INT, 0, 1)), EquatableValueSet.of(allocator, INT, 0, 1).complement(allocator)); + assertEquals(EquatableValueSet.of(allocator, INT, 0).complement(allocator).subtract(allocator, EquatableValueSet.of(allocator, INT, 0, 1).complement(allocator)), EquatableValueSet.of(allocator, INT, 1)); + } +} diff --git a/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/domain/predicate/MarkerTest.java b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/domain/predicate/MarkerTest.java new file mode 100644 index 0000000000..d89b30434a --- /dev/null +++ b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/domain/predicate/MarkerTest.java @@ -0,0 +1,176 @@ +package com.amazonaws.athena.connector.lambda.domain.predicate; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Ordering; +import org.apache.arrow.vector.types.Types; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.util.Map; + +import static org.junit.Assert.*; + +public class MarkerTest +{ + private BlockAllocatorImpl allocator; + + @Before + public void setup() + { + allocator = new BlockAllocatorImpl(); + } + + @After + public void tearDown() + { + allocator.close(); + } + + @Test + public void testTypes() + throws Exception + { + assertEquals(Marker.lowerUnbounded(allocator, Types.MinorType.INT.getType()).getType(), Types.MinorType.INT.getType()); + assertEquals(Marker.below(allocator, Types.MinorType.INT.getType(), 1).getType(), Types.MinorType.INT.getType()); + assertEquals(Marker.exactly(allocator, Types.MinorType.INT.getType(), 1).getType(), Types.MinorType.INT.getType()); + assertEquals(Marker.above(allocator, Types.MinorType.INT.getType(), 1).getType(), Types.MinorType.INT.getType()); + assertEquals(Marker.upperUnbounded(allocator, Types.MinorType.INT.getType()).getType(), Types.MinorType.INT.getType()); + } + + @Test + public void testUnbounded() + throws Exception + { + assertTrue(Marker.lowerUnbounded(allocator, Types.MinorType.INT.getType()).isLowerUnbounded()); + assertFalse(Marker.lowerUnbounded(allocator, Types.MinorType.INT.getType()).isUpperUnbounded()); + assertTrue(Marker.upperUnbounded(allocator, Types.MinorType.INT.getType()).isUpperUnbounded()); + assertFalse(Marker.upperUnbounded(allocator, Types.MinorType.INT.getType()).isLowerUnbounded()); + + assertFalse(Marker.below(allocator, Types.MinorType.INT.getType(), 1).isLowerUnbounded()); + assertFalse(Marker.below(allocator, Types.MinorType.INT.getType(), 1).isUpperUnbounded()); + assertFalse(Marker.exactly(allocator, Types.MinorType.INT.getType(), 1).isLowerUnbounded()); + assertFalse(Marker.exactly(allocator, Types.MinorType.INT.getType(), 1).isUpperUnbounded()); + assertFalse(Marker.above(allocator, Types.MinorType.INT.getType(), 1).isLowerUnbounded()); + assertFalse(Marker.above(allocator, Types.MinorType.INT.getType(), 1).isUpperUnbounded()); + } + + @Test + public void testComparisons() + throws Exception + { + ImmutableList markers = ImmutableList.of( + Marker.lowerUnbounded(allocator, Types.MinorType.INT.getType()), + Marker.above(allocator, Types.MinorType.INT.getType(), 0), + Marker.below(allocator, Types.MinorType.INT.getType(), 1), + Marker.exactly(allocator, Types.MinorType.INT.getType(), 1), + Marker.above(allocator, Types.MinorType.INT.getType(), 1), + Marker.below(allocator, Types.MinorType.INT.getType(), 2), + Marker.upperUnbounded(allocator, Types.MinorType.INT.getType())); + + assertTrue(Ordering.natural().isStrictlyOrdered(markers)); + + // Compare every marker with every other marker + // Since the markers are strictly ordered, the value of the comparisons should be equivalent to the comparisons + // of their indexes. + for (int i = 0; i < markers.size(); i++) { + for (int j = 0; j < markers.size(); j++) { + assertTrue(markers.get(i).compareTo(markers.get(j)) == Integer.compare(i, j)); + } + } + } + + @Test + public void testAdjacency() + throws Exception + { + ImmutableMap markers = ImmutableMap.builder() + .put(Marker.lowerUnbounded(allocator, Types.MinorType.INT.getType()), -1000) + .put(Marker.above(allocator, Types.MinorType.INT.getType(), 0), -100) + .put(Marker.below(allocator, Types.MinorType.INT.getType(), 1), -1) + .put(Marker.exactly(allocator, Types.MinorType.INT.getType(), 1), 0) + .put(Marker.above(allocator, Types.MinorType.INT.getType(), 1), 1) + .put(Marker.below(allocator, Types.MinorType.INT.getType(), 2), 100) + .put(Marker.upperUnbounded(allocator, Types.MinorType.INT.getType()), 1000) + .build(); + + // Compare every marker with every other marker + // Map values of distance 1 indicate expected adjacency + for (Map.Entry entry1 : markers.entrySet()) { + for (Map.Entry entry2 : markers.entrySet()) { + boolean adjacent = entry1.getKey().isAdjacent(entry2.getKey()); + boolean distanceIsOne = Math.abs(entry1.getValue() - entry2.getValue()) == 1; + assertEquals(adjacent, distanceIsOne); + } + } + + assertEquals(Marker.below(allocator, Types.MinorType.INT.getType(), 1).greaterAdjacent(), Marker.exactly(allocator, Types.MinorType.INT.getType(), 1)); + assertEquals(Marker.exactly(allocator, Types.MinorType.INT.getType(), 1).greaterAdjacent(), Marker.above(allocator, Types.MinorType.INT.getType(), 1)); + assertEquals(Marker.above(allocator, Types.MinorType.INT.getType(), 1).lesserAdjacent(), Marker.exactly(allocator, Types.MinorType.INT.getType(), 1)); + assertEquals(Marker.exactly(allocator, Types.MinorType.INT.getType(), 1).lesserAdjacent(), Marker.below(allocator, Types.MinorType.INT.getType(), 1)); + + try { + Marker.below(allocator, Types.MinorType.INT.getType(), 1).lesserAdjacent(); + fail(); + } + catch (IllegalStateException e) { + } + + try { + Marker.above(allocator, Types.MinorType.INT.getType(), 1).greaterAdjacent(); + fail(); + } + catch (IllegalStateException e) { + } + + try { + Marker.lowerUnbounded(allocator, Types.MinorType.INT.getType()).lesserAdjacent(); + fail(); + } + catch (IllegalStateException e) { + } + + try { + Marker.lowerUnbounded(allocator, Types.MinorType.INT.getType()).greaterAdjacent(); + fail(); + } + catch (IllegalStateException e) { + } + + try { + Marker.upperUnbounded(allocator, Types.MinorType.INT.getType()).lesserAdjacent(); + fail(); + } + catch (IllegalStateException e) { + } + + try { + Marker.upperUnbounded(allocator, Types.MinorType.INT.getType()).greaterAdjacent(); + fail(); + } + catch (IllegalStateException e) { + } + } +} diff --git a/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/domain/predicate/RangeTest.java b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/domain/predicate/RangeTest.java new file mode 100644 index 0000000000..b525ecb667 --- /dev/null +++ b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/domain/predicate/RangeTest.java @@ -0,0 +1,297 @@ +package com.amazonaws.athena.connector.lambda.domain.predicate; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import static org.apache.arrow.vector.types.Types.MinorType.BIGINT; +import static org.apache.arrow.vector.types.Types.MinorType.BIT; +import static org.apache.arrow.vector.types.Types.MinorType.FLOAT8; +import static org.apache.arrow.vector.types.Types.MinorType.VARCHAR; +import static org.junit.Assert.*; + +public class RangeTest +{ + private BlockAllocatorImpl allocator; + + @Before + public void setup() + { + allocator = new BlockAllocatorImpl(); + } + + @After + public void tearDown() + { + allocator.close(); + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + @Test(expected = IllegalArgumentException.class) + public void testMismatchedTypes() + throws Exception + { + // NEVER DO THIS + new Range(Marker.exactly(allocator, BIGINT.getType(), 1L), Marker.exactly(allocator, VARCHAR.getType(), "a")); + } + + @Test(expected = IllegalArgumentException.class) + public void testInvertedBounds() + throws Exception + { + new Range(Marker.exactly(allocator, BIGINT.getType(), 1L), Marker.exactly(allocator, BIGINT.getType(), 0L)); + } + + @Test(expected = IllegalArgumentException.class) + public void testLowerUnboundedOnly() + throws Exception + { + new Range(Marker.lowerUnbounded(allocator, BIGINT.getType()), Marker.lowerUnbounded(allocator, BIGINT.getType())); + } + + @Test(expected = IllegalArgumentException.class) + public void testUpperUnboundedOnly() + throws Exception + { + new Range(Marker.upperUnbounded(allocator, BIGINT.getType()), Marker.upperUnbounded(allocator, BIGINT.getType())); + } + + @Test + public void testSingleValue() + throws Exception + { + assertTrue(Range.range(allocator, BIGINT.getType(), 1L, true, 1L, true).isSingleValue()); + assertFalse(Range.range(allocator, BIGINT.getType(), 1L, true, 2L, true).isSingleValue()); + assertTrue(Range.range(allocator, FLOAT8.getType(), 1.1, true, 1.1, true).isSingleValue()); + assertTrue(Range.range(allocator, VARCHAR.getType(), "a", true, "a", true).isSingleValue()); + assertTrue(Range.range(allocator, BIT.getType(), true, true, true, true).isSingleValue()); + assertFalse(Range.range(allocator, BIT.getType(), false, true, true, true).isSingleValue()); + } + + @Test + public void testAllRange() + throws Exception + { + Range range = Range.all(allocator, BIGINT.getType()); + assertEquals(range.getLow(), Marker.lowerUnbounded(allocator, BIGINT.getType())); + assertEquals(range.getHigh(), Marker.upperUnbounded(allocator, BIGINT.getType())); + assertFalse(range.isSingleValue()); + assertTrue(range.isAll()); + assertEquals(range.getType(), BIGINT.getType()); + assertTrue(range.includes(Marker.lowerUnbounded(allocator, BIGINT.getType()))); + assertTrue(range.includes(Marker.below(allocator, BIGINT.getType(), 1L))); + assertTrue(range.includes(Marker.exactly(allocator, BIGINT.getType(), 1L))); + assertTrue(range.includes(Marker.above(allocator, BIGINT.getType(), 1L))); + assertTrue(range.includes(Marker.upperUnbounded(allocator, BIGINT.getType()))); + } + + @Test + public void testGreaterThanRange() + throws Exception + { + Range range = Range.greaterThan(allocator, BIGINT.getType(), 1L); + assertEquals(range.getLow(), Marker.above(allocator, BIGINT.getType(), 1L)); + assertEquals(range.getHigh(), Marker.upperUnbounded(allocator, BIGINT.getType())); + assertFalse(range.isSingleValue()); + assertFalse(range.isAll()); + assertEquals(range.getType(), BIGINT.getType()); + assertFalse(range.includes(Marker.lowerUnbounded(allocator, BIGINT.getType()))); + assertFalse(range.includes(Marker.exactly(allocator, BIGINT.getType(), 1L))); + assertTrue(range.includes(Marker.exactly(allocator, BIGINT.getType(), 2L))); + assertTrue(range.includes(Marker.upperUnbounded(allocator, BIGINT.getType()))); + } + + @Test + public void testGreaterThanOrEqualRange() + throws Exception + { + Range range = Range.greaterThanOrEqual(allocator, BIGINT.getType(), 1L); + assertEquals(range.getLow(), Marker.exactly(allocator, BIGINT.getType(), 1L)); + assertEquals(range.getHigh(), Marker.upperUnbounded(allocator, BIGINT.getType())); + assertFalse(range.isSingleValue()); + assertFalse(range.isAll()); + assertEquals(range.getType(), BIGINT.getType()); + assertFalse(range.includes(Marker.lowerUnbounded(allocator, BIGINT.getType()))); + assertFalse(range.includes(Marker.exactly(allocator, BIGINT.getType(), 0L))); + assertTrue(range.includes(Marker.exactly(allocator, BIGINT.getType(), 1L))); + assertTrue(range.includes(Marker.exactly(allocator, BIGINT.getType(), 2L))); + assertTrue(range.includes(Marker.upperUnbounded(allocator, BIGINT.getType()))); + } + + @Test + public void testLessThanRange() + throws Exception + { + Range range = Range.lessThan(allocator, BIGINT.getType(), 1L); + assertEquals(range.getLow(), Marker.lowerUnbounded(allocator, BIGINT.getType())); + assertEquals(range.getHigh(), Marker.below(allocator, BIGINT.getType(), 1L)); + assertFalse(range.isSingleValue()); + assertFalse(range.isAll()); + assertEquals(range.getType(), BIGINT.getType()); + assertTrue(range.includes(Marker.lowerUnbounded(allocator, BIGINT.getType()))); + assertFalse(range.includes(Marker.exactly(allocator, BIGINT.getType(), 1L))); + assertTrue(range.includes(Marker.exactly(allocator, BIGINT.getType(), 0L))); + assertFalse(range.includes(Marker.upperUnbounded(allocator, BIGINT.getType()))); + } + + @Test + public void testLessThanOrEqualRange() + throws Exception + { + Range range = Range.lessThanOrEqual(allocator, BIGINT.getType(), 1L); + assertEquals(range.getLow(), Marker.lowerUnbounded(allocator, BIGINT.getType())); + assertEquals(range.getHigh(), Marker.exactly(allocator, BIGINT.getType(), 1L)); + assertFalse(range.isSingleValue()); + assertFalse(range.isAll()); + assertEquals(range.getType(), BIGINT.getType()); + assertTrue(range.includes(Marker.lowerUnbounded(allocator, BIGINT.getType()))); + assertFalse(range.includes(Marker.exactly(allocator, BIGINT.getType(), 2L))); + assertTrue(range.includes(Marker.exactly(allocator, BIGINT.getType(), 1L))); + assertTrue(range.includes(Marker.exactly(allocator, BIGINT.getType(), 0L))); + assertFalse(range.includes(Marker.upperUnbounded(allocator, BIGINT.getType()))); + } + + @Test + public void testEqualRange() + throws Exception + { + Range range = Range.equal(allocator, BIGINT.getType(), 1L); + assertEquals(range.getLow(), Marker.exactly(allocator, BIGINT.getType(), 1L)); + assertEquals(range.getHigh(), Marker.exactly(allocator, BIGINT.getType(), 1L)); + assertTrue(range.isSingleValue()); + assertFalse(range.isAll()); + assertEquals(range.getType(), BIGINT.getType()); + assertFalse(range.includes(Marker.lowerUnbounded(allocator, BIGINT.getType()))); + assertFalse(range.includes(Marker.exactly(allocator, BIGINT.getType(), 0L))); + assertTrue(range.includes(Marker.exactly(allocator, BIGINT.getType(), 1L))); + assertFalse(range.includes(Marker.exactly(allocator, BIGINT.getType(), 2L))); + assertFalse(range.includes(Marker.upperUnbounded(allocator, BIGINT.getType()))); + } + + @Test + public void testRange() + throws Exception + { + Range range = Range.range(allocator, BIGINT.getType(), 0L, false, 2L, true); + assertEquals(range.getLow(), Marker.above(allocator, BIGINT.getType(), 0L)); + assertEquals(range.getHigh(), Marker.exactly(allocator, BIGINT.getType(), 2L)); + assertFalse(range.isSingleValue()); + assertFalse(range.isAll()); + assertEquals(range.getType(), BIGINT.getType()); + assertFalse(range.includes(Marker.lowerUnbounded(allocator, BIGINT.getType()))); + assertFalse(range.includes(Marker.exactly(allocator, BIGINT.getType(), 0L))); + assertTrue(range.includes(Marker.exactly(allocator, BIGINT.getType(), 1L))); + assertTrue(range.includes(Marker.exactly(allocator, BIGINT.getType(), 2L))); + assertFalse(range.includes(Marker.exactly(allocator, BIGINT.getType(), 3L))); + assertFalse(range.includes(Marker.upperUnbounded(allocator, BIGINT.getType()))); + } + + @Test + public void testGetSingleValue() + throws Exception + { + assertEquals(Range.equal(allocator, BIGINT.getType(), 0L).getSingleValue(), 0L); + try { + Range.lessThan(allocator, BIGINT.getType(), 0L).getSingleValue(); + fail(); + } + catch (IllegalStateException e) { + } + } + + @Test + public void testContains() + throws Exception + { + assertTrue(Range.all(allocator, BIGINT.getType()).contains(Range.all(allocator, BIGINT.getType()))); + assertTrue(Range.all(allocator, BIGINT.getType()).contains(Range.equal(allocator, BIGINT.getType(), 0L))); + assertTrue(Range.all(allocator, BIGINT.getType()).contains(Range.greaterThan(allocator, BIGINT.getType(), 0L))); + assertTrue(Range.equal(allocator, BIGINT.getType(), 0L).contains(Range.equal(allocator, BIGINT.getType(), 0L))); + assertFalse(Range.equal(allocator, BIGINT.getType(), 0L).contains(Range.greaterThan(allocator, BIGINT.getType(), 0L))); + assertFalse(Range.equal(allocator, BIGINT.getType(), 0L).contains(Range.greaterThanOrEqual(allocator, BIGINT.getType(), 0L))); + assertFalse(Range.equal(allocator, BIGINT.getType(), 0L).contains(Range.all(allocator, BIGINT.getType()))); + assertTrue(Range.greaterThanOrEqual(allocator, BIGINT.getType(), 0L).contains(Range.greaterThan(allocator, BIGINT.getType(), 0L))); + assertTrue(Range.greaterThan(allocator, BIGINT.getType(), 0L).contains(Range.greaterThan(allocator, BIGINT.getType(), 1L))); + assertFalse(Range.greaterThan(allocator, BIGINT.getType(), 0L).contains(Range.lessThan(allocator, BIGINT.getType(), 0L))); + assertTrue(Range.range(allocator, BIGINT.getType(), 0L, true, 2L, true).contains(Range.range(allocator, BIGINT.getType(), 1L, true, 2L, true))); + assertFalse(Range.range(allocator, BIGINT.getType(), 0L, true, 2L, true).contains(Range.range(allocator, BIGINT.getType(), 1L, true, 3L, false))); + } + + @Test + public void testSpan() + throws Exception + { + assertEquals(Range.greaterThan(allocator, BIGINT.getType(), 1L).span(Range.lessThanOrEqual(allocator, BIGINT.getType(), 2L)), Range.all(allocator, BIGINT.getType())); + assertEquals(Range.greaterThan(allocator, BIGINT.getType(), 2L).span(Range.lessThanOrEqual(allocator, BIGINT.getType(), 0L)), Range.all(allocator, BIGINT.getType())); + assertEquals(Range.range(allocator, BIGINT.getType(), 1L, true, 3L, false).span(Range.equal(allocator, BIGINT.getType(), 2L)), Range.range(allocator, BIGINT.getType(), 1L, true, 3L, false)); + assertEquals(Range.range(allocator, BIGINT.getType(), 1L, true, 3L, false).span(Range.range(allocator, BIGINT.getType(), 2L, false, 10L, false)), Range.range(allocator, BIGINT.getType(), 1L, true, 10L, false)); + assertEquals(Range.greaterThan(allocator, BIGINT.getType(), 1L).span(Range.equal(allocator, BIGINT.getType(), 0L)), Range.greaterThanOrEqual(allocator, BIGINT.getType(), 0L)); + assertEquals(Range.greaterThan(allocator, BIGINT.getType(), 1L).span(Range.greaterThanOrEqual(allocator, BIGINT.getType(), 10L)), Range.greaterThan(allocator, BIGINT.getType(), 1L)); + assertEquals(Range.lessThan(allocator, BIGINT.getType(), 1L).span(Range.lessThanOrEqual(allocator, BIGINT.getType(), 1L)), Range.lessThanOrEqual(allocator, BIGINT.getType(), 1L)); + assertEquals(Range.all(allocator, BIGINT.getType()).span(Range.lessThanOrEqual(allocator, BIGINT.getType(), 1L)), Range.all(allocator, BIGINT.getType())); + } + + @Test + public void testOverlaps() + throws Exception + { + assertTrue(Range.greaterThan(allocator, BIGINT.getType(), 1L).overlaps(Range.lessThanOrEqual(allocator, BIGINT.getType(), 2L))); + assertFalse(Range.greaterThan(allocator, BIGINT.getType(), 2L).overlaps(Range.lessThan(allocator, BIGINT.getType(), 2L))); + assertTrue(Range.range(allocator, BIGINT.getType(), 1L, true, 3L, false).overlaps(Range.equal(allocator, BIGINT.getType(), 2L))); + assertTrue(Range.range(allocator, BIGINT.getType(), 1L, true, 3L, false).overlaps(Range.range(allocator, BIGINT.getType(), 2L, false, 10L, false))); + assertFalse(Range.range(allocator, BIGINT.getType(), 1L, true, 3L, false).overlaps(Range.range(allocator, BIGINT.getType(), 3L, true, 10L, false))); + assertTrue(Range.range(allocator, BIGINT.getType(), 1L, true, 3L, true).overlaps(Range.range(allocator, BIGINT.getType(), 3L, true, 10L, false))); + assertTrue(Range.all(allocator, BIGINT.getType()).overlaps(Range.equal(allocator, BIGINT.getType(), Long.MAX_VALUE))); + } + + @Test + public void testIntersect() + throws Exception + { + assertEquals(Range.greaterThan(allocator, BIGINT.getType(), 1L).intersect(Range.lessThanOrEqual(allocator, BIGINT.getType(), 2L)), Range.range(allocator, BIGINT.getType(), 1L, false, 2L, true)); + assertEquals(Range.range(allocator, BIGINT.getType(), 1L, true, 3L, false).intersect(Range.equal(allocator, BIGINT.getType(), 2L)), Range.equal(allocator, BIGINT.getType(), 2L)); + assertEquals(Range.range(allocator, BIGINT.getType(), 1L, true, 3L, false).intersect(Range.range(allocator, BIGINT.getType(), 2L, false, 10L, false)), Range.range(allocator, BIGINT.getType(), 2L, false, 3L, false)); + assertEquals(Range.range(allocator, BIGINT.getType(), 1L, true, 3L, true).intersect(Range.range(allocator, BIGINT.getType(), 3L, true, 10L, false)), Range.equal(allocator, BIGINT.getType(), 3L)); + assertEquals(Range.all(allocator, BIGINT.getType()).intersect(Range.equal(allocator, BIGINT.getType(), Long.MAX_VALUE)), Range.equal(allocator, BIGINT.getType(), Long.MAX_VALUE)); + } + + @Test + public void testExceptionalIntersect() + throws Exception + { + try { + Range.greaterThan(allocator, BIGINT.getType(), 2L).intersect(Range.lessThan(allocator, BIGINT.getType(), 2L)); + fail(); + } + catch (IllegalArgumentException e) { + } + + try { + Range.range(allocator, BIGINT.getType(), 1L, true, 3L, false).intersect(Range.range(allocator, BIGINT.getType(), 3L, true, 10L, false)); + fail(); + } + catch (IllegalArgumentException e) { + } + } +} diff --git a/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/domain/predicate/SortedRangeSetTest.java b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/domain/predicate/SortedRangeSetTest.java new file mode 100644 index 0000000000..a02862f80b --- /dev/null +++ b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/domain/predicate/SortedRangeSetTest.java @@ -0,0 +1,457 @@ +package com.amazonaws.athena.connector.lambda.domain.predicate; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.util.Collections; +import java.util.stream.Collectors; + +import static org.apache.arrow.vector.types.Types.MinorType.BIGINT; +import static org.junit.Assert.*; + +public class SortedRangeSetTest +{ + private BlockAllocatorImpl allocator; + + @Before + public void setup() + { + allocator = new BlockAllocatorImpl(); + } + + @After + public void tearDown() + { + allocator.close(); + } + + @Test + public void testEmptySet() + throws Exception + { + SortedRangeSet rangeSet = SortedRangeSet.none(BIGINT.getType()); + assertEquals(rangeSet.getType(), BIGINT.getType()); + assertTrue(rangeSet.isNone()); + assertFalse(rangeSet.isAll()); + assertFalse(rangeSet.isSingleValue()); + assertTrue(Iterables.isEmpty(rangeSet.getOrderedRanges())); + assertEquals(rangeSet.getRangeCount(), 0); + assertEquals(rangeSet.complement(allocator), SortedRangeSet.all(allocator, BIGINT.getType())); + assertFalse(rangeSet.includesMarker(Marker.lowerUnbounded(allocator, BIGINT.getType()))); + assertFalse(rangeSet.includesMarker(Marker.exactly(allocator, BIGINT.getType(), 0L))); + assertFalse(rangeSet.includesMarker(Marker.upperUnbounded(allocator, BIGINT.getType()))); + } + + @Test + public void testEntireSet() + throws Exception + { + SortedRangeSet rangeSet = SortedRangeSet.all(allocator, BIGINT.getType()); + assertEquals(rangeSet.getType(), BIGINT.getType()); + assertFalse(rangeSet.isNone()); + assertTrue(rangeSet.isAll()); + assertFalse(rangeSet.isSingleValue()); + assertEquals(rangeSet.getRangeCount(), 1); + assertEquals(rangeSet.complement(allocator), SortedRangeSet.none(BIGINT.getType())); + assertTrue(rangeSet.includesMarker(Marker.lowerUnbounded(allocator, BIGINT.getType()))); + assertTrue(rangeSet.includesMarker(Marker.exactly(allocator, BIGINT.getType(), 0L))); + assertTrue(rangeSet.includesMarker(Marker.upperUnbounded(allocator, BIGINT.getType()))); + } + + @Test + public void testNullability() + throws Exception + { + SortedRangeSet rangeSet = SortedRangeSet.of(allocator, BIGINT.getType(), true, 10L, Collections.singletonList(10L)); + assertTrue(rangeSet.containsValue(Marker.nullMarker(allocator, BIGINT.getType()))); + assertFalse(rangeSet.containsValue(Marker.exactly(allocator, BIGINT.getType(), 1L))); + assertTrue(rangeSet.containsValue(Marker.exactly(allocator, BIGINT.getType(), 10L))); + } + + @Test + public void testSingleValue() + throws Exception + { + SortedRangeSet rangeSet = SortedRangeSet.of(allocator, BIGINT.getType(), 10L); + + SortedRangeSet complement = SortedRangeSet.of(true, + Range.greaterThan(allocator, BIGINT.getType(), 10L), + Range.lessThan(allocator, BIGINT.getType(), 10L)); + + assertEquals(rangeSet.getType(), BIGINT.getType()); + assertFalse(rangeSet.isNone()); + assertFalse(rangeSet.isAll()); + assertTrue(rangeSet.isSingleValue()); + assertTrue(Iterables.elementsEqual(rangeSet.getOrderedRanges(), ImmutableList.of(Range.equal(allocator, BIGINT.getType(), 10L)))); + assertEquals(rangeSet.getRangeCount(), 1); + assertEquals(rangeSet.complement(allocator), complement); + assertFalse(rangeSet.includesMarker(Marker.lowerUnbounded(allocator, BIGINT.getType()))); + assertTrue(rangeSet.includesMarker(Marker.exactly(allocator, BIGINT.getType(), 10L))); + assertFalse(rangeSet.includesMarker(Marker.exactly(allocator, BIGINT.getType(), 9L))); + assertFalse(rangeSet.includesMarker(Marker.upperUnbounded(allocator, BIGINT.getType()))); + } + + @Test + public void testBoundedSet() + throws Exception + { + SortedRangeSet rangeSet = SortedRangeSet.of( + Range.equal(allocator, BIGINT.getType(), 10L), + Range.equal(allocator, BIGINT.getType(), 0L), + Range.range(allocator, BIGINT.getType(), 9L, true, 11L, false), + Range.equal(allocator, BIGINT.getType(), 0L), + Range.range(allocator, BIGINT.getType(), 2L, true, 4L, true), + Range.range(allocator, BIGINT.getType(), 4L, false, 5L, true)); + + ImmutableList normalizedResult = ImmutableList.of( + Range.equal(allocator, BIGINT.getType(), 0L), + Range.range(allocator, BIGINT.getType(), 2L, true, 5L, true), + Range.range(allocator, BIGINT.getType(), 9L, true, 11L, false)); + + SortedRangeSet complement = SortedRangeSet.of(true, + Range.lessThan(allocator, BIGINT.getType(), 0L), + Range.range(allocator, BIGINT.getType(), 0L, false, 2L, false), + Range.range(allocator, BIGINT.getType(), 5L, false, 9L, false), + Range.greaterThanOrEqual(allocator, BIGINT.getType(), 11L)); + + assertEquals(rangeSet.getType(), BIGINT.getType()); + assertFalse(rangeSet.isNone()); + assertFalse(rangeSet.isAll()); + assertFalse(rangeSet.isSingleValue()); + assertTrue(Iterables.elementsEqual(rangeSet.getOrderedRanges(), normalizedResult)); + assertEquals(rangeSet, SortedRangeSet.copyOf(BIGINT.getType(), normalizedResult, false)); + assertEquals(rangeSet.getRangeCount(), 3); + assertEquals(rangeSet.complement(allocator), complement); + assertFalse(rangeSet.includesMarker(Marker.lowerUnbounded(allocator, BIGINT.getType()))); + assertTrue(rangeSet.includesMarker(Marker.exactly(allocator, BIGINT.getType(), 0L))); + assertFalse(rangeSet.includesMarker(Marker.exactly(allocator, BIGINT.getType(), 1L))); + assertFalse(rangeSet.includesMarker(Marker.exactly(allocator, BIGINT.getType(), 7L))); + assertTrue(rangeSet.includesMarker(Marker.exactly(allocator, BIGINT.getType(), 9L))); + assertFalse(rangeSet.includesMarker(Marker.upperUnbounded(allocator, BIGINT.getType()))); + } + + @Test + public void testUnboundedSet() + throws Exception + { + SortedRangeSet rangeSet = SortedRangeSet.of( + Range.greaterThan(allocator, BIGINT.getType(), 10L), + Range.lessThanOrEqual(allocator, BIGINT.getType(), 0L), + Range.range(allocator, BIGINT.getType(), 2L, true, 4L, false), + Range.range(allocator, BIGINT.getType(), 4L, true, 6L, false), + Range.range(allocator, BIGINT.getType(), 1L, false, 2L, false), + Range.range(allocator, BIGINT.getType(), 9L, false, 11L, false)); + + ImmutableList normalizedResult = ImmutableList.of( + Range.lessThanOrEqual(allocator, BIGINT.getType(), 0L), + Range.range(allocator, BIGINT.getType(), 1L, false, 6L, false), + Range.greaterThan(allocator, BIGINT.getType(), 9L)); + + SortedRangeSet complement = SortedRangeSet.of(true, + Range.range(allocator, BIGINT.getType(), 0L, false, 1L, true), + Range.range(allocator, BIGINT.getType(), 6L, true, 9L, true)); + + assertEquals(rangeSet.getType(), BIGINT.getType()); + assertFalse(rangeSet.isNone()); + assertFalse(rangeSet.isAll()); + assertFalse(rangeSet.isSingleValue()); + assertTrue(Iterables.elementsEqual(rangeSet.getOrderedRanges(), normalizedResult)); + assertEquals(rangeSet, SortedRangeSet.copyOf(BIGINT.getType(), normalizedResult, false)); + assertEquals(rangeSet.getRangeCount(), 3); + assertEquals(rangeSet.complement(allocator), complement); + assertTrue(rangeSet.includesMarker(Marker.lowerUnbounded(allocator, BIGINT.getType()))); + assertTrue(rangeSet.includesMarker(Marker.exactly(allocator, BIGINT.getType(), 0L))); + assertTrue(rangeSet.includesMarker(Marker.exactly(allocator, BIGINT.getType(), 4L))); + assertFalse(rangeSet.includesMarker(Marker.exactly(allocator, BIGINT.getType(), 7L))); + assertTrue(rangeSet.includesMarker(Marker.upperUnbounded(allocator, BIGINT.getType()))); + } + + @Test + public void testGetSingleValue() + throws Exception + { + assertEquals(SortedRangeSet.of(allocator, BIGINT.getType(), 0L).getSingleValue(), 0L); + try { + SortedRangeSet.all(allocator, BIGINT.getType()).getSingleValue(); + fail(); + } + catch (IllegalStateException e) { + } + } + + @Test + public void testSpan() + throws Exception + { + try { + SortedRangeSet.none(BIGINT.getType()).getSpan(); + fail(); + } + catch (IllegalStateException e) { + } + + assertEquals(SortedRangeSet.all(allocator, BIGINT.getType()).getSpan(), Range.all(allocator, BIGINT.getType())); + assertEquals(SortedRangeSet.of(allocator, BIGINT.getType(), 0L).getSpan(), Range.equal(allocator, BIGINT.getType(), 0L)); + assertEquals(SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 0L), Range.equal(allocator, BIGINT.getType(), 1L)).getSpan(), Range.range(allocator, BIGINT.getType(), 0L, true, 1L, true)); + assertEquals(SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 0L), Range.greaterThan(allocator, BIGINT.getType(), 1L)).getSpan(), Range.greaterThanOrEqual(allocator, BIGINT.getType(), 0L)); + assertEquals(SortedRangeSet.of(Range.lessThan(allocator, BIGINT.getType(), 0L), Range.greaterThan(allocator, BIGINT.getType(), 1L)).getSpan(), Range.all(allocator, BIGINT.getType())); + } + + @Test + public void testOverlaps() + throws Exception + { + assertTrue(SortedRangeSet.all(allocator, BIGINT.getType()).overlaps(allocator, SortedRangeSet.all(allocator, BIGINT.getType()))); + assertFalse(SortedRangeSet.all(allocator, BIGINT.getType()).overlaps(allocator, SortedRangeSet.none(BIGINT.getType()))); + assertTrue(SortedRangeSet.all(allocator, BIGINT.getType()).overlaps(allocator, SortedRangeSet.of(allocator, BIGINT.getType(), 0L))); + assertTrue(SortedRangeSet.all(allocator, BIGINT.getType()).overlaps(allocator, SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 0L), Range.equal(allocator, BIGINT.getType(), 1L)))); + assertTrue(SortedRangeSet.all(allocator, BIGINT.getType()).overlaps(allocator, SortedRangeSet.of(Range.greaterThan(allocator, BIGINT.getType(), 0L)))); + assertTrue(SortedRangeSet.all(allocator, BIGINT.getType()).overlaps(allocator, SortedRangeSet.of(Range.greaterThan(allocator, BIGINT.getType(), 0L), Range.lessThan(allocator, BIGINT.getType(), 0L)))); + + assertFalse(SortedRangeSet.none(BIGINT.getType()).overlaps(allocator, SortedRangeSet.all(allocator, BIGINT.getType()))); + assertFalse(SortedRangeSet.none(BIGINT.getType()).overlaps(allocator, SortedRangeSet.none(BIGINT.getType()))); + assertFalse(SortedRangeSet.none(BIGINT.getType()).overlaps(allocator, SortedRangeSet.of(allocator, BIGINT.getType(), 0L))); + assertFalse(SortedRangeSet.none(BIGINT.getType()).overlaps(allocator, SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 0L), Range.equal(allocator, BIGINT.getType(), 1L)))); + assertFalse(SortedRangeSet.none(BIGINT.getType()).overlaps(allocator, SortedRangeSet.of(Range.greaterThan(allocator, BIGINT.getType(), 0L)))); + assertFalse(SortedRangeSet.none(BIGINT.getType()).overlaps(allocator, SortedRangeSet.of(Range.greaterThan(allocator, BIGINT.getType(), 0L), Range.lessThan(allocator, BIGINT.getType(), 0L)))); + + assertTrue(SortedRangeSet.of(allocator, BIGINT.getType(), 0L).overlaps(allocator, SortedRangeSet.all(allocator, BIGINT.getType()))); + assertFalse(SortedRangeSet.of(allocator, BIGINT.getType(), 0L).overlaps(allocator, SortedRangeSet.none(BIGINT.getType()))); + assertTrue(SortedRangeSet.of(allocator, BIGINT.getType(), 0L).overlaps(allocator, SortedRangeSet.of(allocator, BIGINT.getType(), 0L))); + assertTrue(SortedRangeSet.of(allocator, BIGINT.getType(), 0L).overlaps(allocator, SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 0L), Range.equal(allocator, BIGINT.getType(), 1L)))); + assertFalse(SortedRangeSet.of(allocator, BIGINT.getType(), 0L).overlaps(allocator, SortedRangeSet.of(Range.greaterThan(allocator, BIGINT.getType(), 0L)))); + assertFalse(SortedRangeSet.of(allocator, BIGINT.getType(), 0L).overlaps(allocator, SortedRangeSet.of(Range.greaterThan(allocator, BIGINT.getType(), 0L), Range.lessThan(allocator, BIGINT.getType(), 0L)))); + + assertTrue(SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 0L), Range.equal(allocator, BIGINT.getType(), 1L)).overlaps(allocator, SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 1L)))); + assertFalse(SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 0L), Range.equal(allocator, BIGINT.getType(), 1L)).overlaps(allocator, SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 2L)))); + assertTrue(SortedRangeSet.of(Range.greaterThanOrEqual(allocator, BIGINT.getType(), 0L)).overlaps(allocator, SortedRangeSet.of(Range.greaterThan(allocator, BIGINT.getType(), 0L)))); + assertTrue(SortedRangeSet.of(Range.greaterThan(allocator, BIGINT.getType(), 0L)).overlaps(allocator, SortedRangeSet.of(Range.greaterThanOrEqual(allocator, BIGINT.getType(), 0L)))); + assertFalse(SortedRangeSet.of(Range.lessThan(allocator, BIGINT.getType(), 0L)).overlaps(allocator, SortedRangeSet.of(Range.greaterThan(allocator, BIGINT.getType(), 0L)))); + } + + @Test + public void testContains() + throws Exception + { + assertTrue(SortedRangeSet.all(allocator, BIGINT.getType()).contains(allocator, SortedRangeSet.all(allocator, BIGINT.getType()))); + assertTrue(SortedRangeSet.all(allocator, BIGINT.getType()).contains(allocator, SortedRangeSet.none(BIGINT.getType()))); + assertTrue(SortedRangeSet.all(allocator, BIGINT.getType()).contains(allocator, SortedRangeSet.of(allocator, BIGINT.getType(), 0L))); + assertTrue(SortedRangeSet.all(allocator, BIGINT.getType()).contains(allocator, SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 0L), Range.equal(allocator, BIGINT.getType(), 1L)))); + assertTrue(SortedRangeSet.all(allocator, BIGINT.getType()).contains(allocator, SortedRangeSet.of(Range.greaterThan(allocator, BIGINT.getType(), 0L)))); + assertTrue(SortedRangeSet.all(allocator, BIGINT.getType()).contains(allocator, SortedRangeSet.of(Range.greaterThan(allocator, BIGINT.getType(), 0L), Range.lessThan(allocator, BIGINT.getType(), 0L)))); + + assertFalse(SortedRangeSet.none(BIGINT.getType()).contains(allocator, SortedRangeSet.all(allocator, BIGINT.getType()))); + assertTrue(SortedRangeSet.none(BIGINT.getType()).contains(allocator, SortedRangeSet.none(BIGINT.getType()))); + assertFalse(SortedRangeSet.none(BIGINT.getType()).contains(allocator, SortedRangeSet.of(allocator, BIGINT.getType(), 0L))); + assertFalse(SortedRangeSet.none(BIGINT.getType()).contains(allocator, SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 0L), Range.equal(allocator, BIGINT.getType(), 1L)))); + assertFalse(SortedRangeSet.none(BIGINT.getType()).contains(allocator, SortedRangeSet.of(Range.greaterThan(allocator, BIGINT.getType(), 0L)))); + assertFalse(SortedRangeSet.none(BIGINT.getType()).contains(allocator, SortedRangeSet.of(Range.greaterThan(allocator, BIGINT.getType(), 0L), Range.lessThan(allocator, BIGINT.getType(), 0L)))); + + assertFalse(SortedRangeSet.of(allocator, BIGINT.getType(), 0L).contains(allocator, SortedRangeSet.all(allocator, BIGINT.getType()))); + assertTrue(SortedRangeSet.of(allocator, BIGINT.getType(), 0L).contains(allocator, SortedRangeSet.none(BIGINT.getType()))); + assertTrue(SortedRangeSet.of(allocator, BIGINT.getType(), 0L).contains(allocator, SortedRangeSet.of(allocator, BIGINT.getType(), 0L))); + assertFalse(SortedRangeSet.of(allocator, BIGINT.getType(), 0L).contains(allocator, SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 0L), Range.equal(allocator, BIGINT.getType(), 1L)))); + assertFalse(SortedRangeSet.of(allocator, BIGINT.getType(), 0L).contains(allocator, SortedRangeSet.of(Range.greaterThan(allocator, BIGINT.getType(), 0L)))); + assertFalse(SortedRangeSet.of(allocator, BIGINT.getType(), 0L).contains(allocator, SortedRangeSet.of(Range.greaterThan(allocator, BIGINT.getType(), 0L), Range.lessThan(allocator, BIGINT.getType(), 0L)))); + + assertTrue(SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 0L), Range.equal(allocator, BIGINT.getType(), 1L)).contains(allocator, SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 1L)))); + assertFalse(SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 0L), Range.equal(allocator, BIGINT.getType(), 1L)).contains(allocator, SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 1L), Range.equal(allocator, BIGINT.getType(), 2L)))); + assertTrue(SortedRangeSet.of(Range.greaterThanOrEqual(allocator, BIGINT.getType(), 0L)).contains(allocator, SortedRangeSet.of(Range.greaterThan(allocator, BIGINT.getType(), 0L)))); + assertFalse(SortedRangeSet.of(Range.greaterThan(allocator, BIGINT.getType(), 0L)).contains(allocator, SortedRangeSet.of(Range.greaterThanOrEqual(allocator, BIGINT.getType(), 0L)))); + assertFalse(SortedRangeSet.of(Range.lessThan(allocator, BIGINT.getType(), 0L)).contains(allocator, SortedRangeSet.of(Range.greaterThan(allocator, BIGINT.getType(), 0L)))); + } + + @Test + public void testIntersect() + throws Exception + { + assertEquals( + SortedRangeSet.none(BIGINT.getType()).intersect(allocator, + SortedRangeSet.none(BIGINT.getType())), + SortedRangeSet.none(BIGINT.getType())); + + assertEquals( + SortedRangeSet.all(allocator, BIGINT.getType()).intersect(allocator, + SortedRangeSet.all(allocator, BIGINT.getType())), + SortedRangeSet.all(allocator, BIGINT.getType())); + + assertEquals( + SortedRangeSet.none(BIGINT.getType()).intersect(allocator, + SortedRangeSet.all(allocator, BIGINT.getType())), + SortedRangeSet.none(BIGINT.getType())); + + assertEquals( + SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 1L), Range.equal(allocator, BIGINT.getType(), 2L), Range.equal(allocator, BIGINT.getType(), 3L)).intersect(allocator, + SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 2L), Range.equal(allocator, BIGINT.getType(), 4L))), + SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 2L))); + + assertEquals( + SortedRangeSet.all(allocator, BIGINT.getType()).intersect(allocator, + SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 2L), Range.equal(allocator, BIGINT.getType(), 4L))), + SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 2L), Range.equal(allocator, BIGINT.getType(), 4L))); + + assertEquals( + SortedRangeSet.of(Range.range(allocator, BIGINT.getType(), 0L, true, 4L, false)).intersect(allocator, + SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 2L), Range.greaterThan(allocator, BIGINT.getType(), 3L))), + SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 2L), Range.range(allocator, BIGINT.getType(), 3L, false, 4L, false))); + + assertEquals( + SortedRangeSet.of(Range.greaterThanOrEqual(allocator, BIGINT.getType(), 0L)).intersect(allocator, + SortedRangeSet.of(Range.lessThanOrEqual(allocator, BIGINT.getType(), 0L))), + SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 0L))); + + assertEquals( + SortedRangeSet.of(Range.greaterThanOrEqual(allocator, BIGINT.getType(), -1L)).intersect(allocator, + SortedRangeSet.of(Range.lessThanOrEqual(allocator, BIGINT.getType(), 1L))), + SortedRangeSet.of(Range.range(allocator, BIGINT.getType(), -1L, true, 1L, true))); + } + + @Test + public void testUnion() + throws Exception + { + assertUnion(SortedRangeSet.none(BIGINT.getType()), SortedRangeSet.none(BIGINT.getType()), SortedRangeSet.none(BIGINT.getType())); + assertUnion(SortedRangeSet.all(allocator, BIGINT.getType()), SortedRangeSet.all(allocator, BIGINT.getType()), SortedRangeSet.all(allocator, BIGINT.getType())); + assertUnion(SortedRangeSet.none(BIGINT.getType()), SortedRangeSet.all(allocator, BIGINT.getType()), SortedRangeSet.all(allocator, BIGINT.getType())); + + assertUnion( + SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 1L), Range.equal(allocator, BIGINT.getType(), 2L)), + SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 2L), Range.equal(allocator, BIGINT.getType(), 3L)), + SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 1L), Range.equal(allocator, BIGINT.getType(), 2L), Range.equal(allocator, BIGINT.getType(), 3L))); + + assertUnion(SortedRangeSet.all(allocator, BIGINT.getType()), SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 0L)), SortedRangeSet.all(allocator, BIGINT.getType())); + + assertUnion( + SortedRangeSet.of(Range.range(allocator, BIGINT.getType(), 0L, true, 4L, false)), + SortedRangeSet.of(Range.greaterThan(allocator, BIGINT.getType(), 3L)), + SortedRangeSet.of(Range.greaterThanOrEqual(allocator, BIGINT.getType(), 0L))); + + assertUnion( + SortedRangeSet.of(Range.greaterThanOrEqual(allocator, BIGINT.getType(), 0L)), + SortedRangeSet.of(Range.lessThanOrEqual(allocator, BIGINT.getType(), 0L)), + SortedRangeSet.of(Range.all(allocator, BIGINT.getType()))); + + assertUnion( + SortedRangeSet.of(Range.greaterThan(allocator, BIGINT.getType(), 0L)), + SortedRangeSet.of(true, Range.lessThan(allocator, BIGINT.getType(), 0L)), + SortedRangeSet.of(allocator, BIGINT.getType(), 0L).complement(allocator)); + } + + @Test + public void testSubtract() + throws Exception + { + assertEquals( + SortedRangeSet.all(allocator, BIGINT.getType()).subtract(allocator, SortedRangeSet.all(allocator, BIGINT.getType())), + SortedRangeSet.none(BIGINT.getType())); + assertEquals( + SortedRangeSet.all(allocator, BIGINT.getType()).subtract(allocator, SortedRangeSet.none(BIGINT.getType())), + SortedRangeSet.all(allocator, BIGINT.getType())); + assertEquals( + SortedRangeSet.all(allocator, BIGINT.getType()).subtract(allocator, SortedRangeSet.of(allocator, BIGINT.getType(), 0L)), + SortedRangeSet.of(allocator, BIGINT.getType(), 0L).complement(allocator)); + assertEquals( + SortedRangeSet.all(allocator, BIGINT.getType()).subtract(allocator, SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 0L), Range.equal(allocator, BIGINT.getType(), 1L))), + SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 0L), Range.equal(allocator, BIGINT.getType(), 1L)).complement(allocator)); + assertEquals( + SortedRangeSet.all(allocator, BIGINT.getType()).subtract(allocator, SortedRangeSet.of(Range.greaterThan(allocator, BIGINT.getType(), 0L))), + SortedRangeSet.of(true, Range.lessThanOrEqual(allocator, BIGINT.getType(), 0L))); + + assertEquals( + SortedRangeSet.none(BIGINT.getType()).subtract(allocator, SortedRangeSet.all(allocator, BIGINT.getType())), + SortedRangeSet.none(BIGINT.getType())); + assertEquals( + SortedRangeSet.none(BIGINT.getType()).subtract(allocator, SortedRangeSet.none(BIGINT.getType())), + SortedRangeSet.none(BIGINT.getType())); + assertEquals( + SortedRangeSet.none(BIGINT.getType()).subtract(allocator, SortedRangeSet.of(allocator, BIGINT.getType(), 0L)), + SortedRangeSet.none(BIGINT.getType())); + assertEquals( + SortedRangeSet.none(BIGINT.getType()).subtract(allocator, SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 0L), Range.equal(allocator, BIGINT.getType(), 1L))), + SortedRangeSet.none(BIGINT.getType())); + assertEquals( + SortedRangeSet.none(BIGINT.getType()).subtract(allocator, SortedRangeSet.of(Range.greaterThan(allocator, BIGINT.getType(), 0L))), + SortedRangeSet.none(BIGINT.getType())); + + assertEquals( + SortedRangeSet.of(allocator, BIGINT.getType(), 0L).subtract(allocator, SortedRangeSet.all(allocator, BIGINT.getType())), + SortedRangeSet.none(BIGINT.getType())); + assertEquals( + SortedRangeSet.of(allocator, BIGINT.getType(), 0L).subtract(allocator, SortedRangeSet.none(BIGINT.getType())), + SortedRangeSet.of(allocator, BIGINT.getType(), 0L)); + assertEquals( + SortedRangeSet.of(allocator, BIGINT.getType(), 0L).subtract(allocator, SortedRangeSet.of(allocator, BIGINT.getType(), 0L)), + SortedRangeSet.none(BIGINT.getType())); + assertEquals( + SortedRangeSet.of(allocator, BIGINT.getType(), 0L).subtract(allocator, SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 0L), Range.equal(allocator, BIGINT.getType(), 1L))), + SortedRangeSet.none(BIGINT.getType())); + + SortedRangeSet.of(allocator, BIGINT.getType(), 0L).subtract(allocator, SortedRangeSet.of(Range.greaterThan(allocator, BIGINT.getType(), 0L))); + + assertEquals( + SortedRangeSet.of(allocator, BIGINT.getType(), 0L).subtract(allocator, SortedRangeSet.of(Range.greaterThan(allocator, BIGINT.getType(), 0L))), + SortedRangeSet.of(allocator, BIGINT.getType(), 0L)); + + assertEquals( + SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 0L), Range.equal(allocator, BIGINT.getType(), 1L)).subtract(allocator, SortedRangeSet.all(allocator, BIGINT.getType())), + SortedRangeSet.none(BIGINT.getType())); + assertEquals( + SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 0L), Range.equal(allocator, BIGINT.getType(), 1L)).subtract(allocator, SortedRangeSet.none(BIGINT.getType())), + SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 0L), Range.equal(allocator, BIGINT.getType(), 1L))); + assertEquals( + SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 0L), Range.equal(allocator, BIGINT.getType(), 1L)).subtract(allocator, SortedRangeSet.of(allocator, BIGINT.getType(), 0L)), + SortedRangeSet.of(allocator, BIGINT.getType(), 1L)); + assertEquals( + SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 0L), Range.equal(allocator, BIGINT.getType(), 1L)).subtract(allocator, SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 0L), Range.equal(allocator, BIGINT.getType(), 1L))), + SortedRangeSet.none(BIGINT.getType())); + assertEquals( + SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 0L), Range.equal(allocator, BIGINT.getType(), 1L)).subtract(allocator, SortedRangeSet.of(Range.greaterThan(allocator, BIGINT.getType(), 0L))), + SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 0L))); + + assertEquals( + SortedRangeSet.of(Range.greaterThan(allocator, BIGINT.getType(), 0L)).subtract(allocator, SortedRangeSet.all(allocator, BIGINT.getType())), + SortedRangeSet.none(BIGINT.getType())); + assertEquals( + SortedRangeSet.of(Range.greaterThan(allocator, BIGINT.getType(), 0L)).subtract(allocator, SortedRangeSet.none(BIGINT.getType())), + SortedRangeSet.of(Range.greaterThan(allocator, BIGINT.getType(), 0L))); + assertEquals( + SortedRangeSet.of(Range.greaterThan(allocator, BIGINT.getType(), 0L)).subtract(allocator, SortedRangeSet.of(allocator, BIGINT.getType(), 0L)), + SortedRangeSet.of(Range.greaterThan(allocator, BIGINT.getType(), 0L))); + assertEquals( + SortedRangeSet.of(Range.greaterThan(allocator, BIGINT.getType(), 0L)).subtract(allocator, SortedRangeSet.of(Range.equal(allocator, BIGINT.getType(), 0L), Range.equal(allocator, BIGINT.getType(), 1L))), + SortedRangeSet.of(Range.range(allocator, BIGINT.getType(), 0L, false, 1L, false), Range.greaterThan(allocator, BIGINT.getType(), 1L))); + assertEquals( + SortedRangeSet.of(Range.greaterThan(allocator, BIGINT.getType(), 0L)).subtract(allocator, SortedRangeSet.of(Range.greaterThan(allocator, BIGINT.getType(), 0L))), + SortedRangeSet.none(BIGINT.getType())); + } + + private void assertUnion(SortedRangeSet first, SortedRangeSet second, SortedRangeSet expected) + { + assertEquals(first.union(allocator, second), expected); + assertEquals(first.union(allocator, ImmutableList.of(first, second)), expected); + } +} diff --git a/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/examples/ExampleMetadataHandlerTest.java b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/examples/ExampleMetadataHandlerTest.java new file mode 100644 index 0000000000..fb8e5122f5 --- /dev/null +++ b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/examples/ExampleMetadataHandlerTest.java @@ -0,0 +1,326 @@ +package com.amazonaws.athena.connector.lambda.examples; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.amazonaws.athena.connector.lambda.data.BlockUtils; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.domain.predicate.Range; +import com.amazonaws.athena.connector.lambda.domain.predicate.SortedRangeSet; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesResponse; +import com.amazonaws.athena.connector.lambda.metadata.MetadataRequest; +import com.amazonaws.athena.connector.lambda.metadata.MetadataRequestType; +import com.amazonaws.athena.connector.lambda.metadata.MetadataResponse; +import com.amazonaws.athena.connector.lambda.metadata.MetadataService; +import com.amazonaws.athena.connector.lambda.security.IdentityUtil; +import com.amazonaws.athena.connector.lambda.security.LocalKeyFactory; +import com.amazonaws.athena.connector.lambda.serde.ObjectMapperUtil; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.lambda.invoke.LambdaFunctionException; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.google.common.collect.ImmutableList; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.arrow.vector.types.pojo.Schema; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static com.amazonaws.athena.connector.lambda.examples.ExampleMetadataHandler.MAX_SPLITS_PER_REQUEST; +import static org.junit.Assert.*; +import static org.mockito.Mockito.mock; + +public class ExampleMetadataHandlerTest +{ + private static final Logger logger = LoggerFactory.getLogger(ExampleMetadataHandlerTest.class); + + private BlockAllocatorImpl allocator; + private ExampleMetadataHandler metadataHandler; + + @Before + public void setUp() + { + logger.info("setUpBefore - enter"); + allocator = new BlockAllocatorImpl(); + metadataHandler = new ExampleMetadataHandler(new LocalKeyFactory(), + mock(AWSSecretsManager.class), + mock(AmazonAthena.class), + "spill-bucket", + "spill-prefix"); + logger.info("setUpBefore - exit"); + } + + @After + public void after() + { + allocator.close(); + } + + @After + public void tearDown() + throws Exception + { + } + + @Test + public void doListSchemas() + { + logger.info("doListSchemas - enter"); + ListSchemasRequest req = new ListSchemasRequest(IdentityUtil.fakeIdentity(), "queryId", "default"); + ObjectMapperUtil.assertSerialization(req, req.getClass()); + ListSchemasResponse res = metadataHandler.doListSchemaNames(allocator, req); + ObjectMapperUtil.assertSerialization(res, res.getClass()); + logger.info("doListSchemas - {}", res.getSchemas()); + assertFalse(res.getSchemas().isEmpty()); + logger.info("doListSchemas - exit"); + } + + @Test + public void doListTables() + { + logger.info("doListTables - enter"); + ListTablesRequest req = new ListTablesRequest(IdentityUtil.fakeIdentity(), "queryId", "default", null); + ObjectMapperUtil.assertSerialization(req, req.getClass()); + ListTablesResponse res = metadataHandler.doListTables(allocator, req); + ObjectMapperUtil.assertSerialization(res, res.getClass()); + logger.info("doListTables - {}", res.getTables()); + assertFalse(res.getTables().isEmpty()); + logger.info("doListTables - exit"); + } + + @Test + public void doGetTable() + { + logger.info("doGetTable - enter"); + GetTableRequest req = new GetTableRequest(IdentityUtil.fakeIdentity(), "queryId", "default", + new TableName("custom_source", "fake_table")); + ObjectMapperUtil.assertSerialization(req, req.getClass()); + GetTableResponse res = metadataHandler.doGetTable(allocator, req); + ObjectMapperUtil.assertSerialization(res, res.getClass()); + assertTrue(res.getSchema().getFields().size() > 0); + assertTrue(res.getSchema().getCustomMetadata().size() > 0); + logger.info("doGetTable - {}", res); + logger.info("doGetTable - exit"); + } + + @Test(expected = LambdaFunctionException.class) + public void doGetTableFail() + { + try { + logger.info("doGetTableFail - enter"); + GetTableRequest req = new GetTableRequest(IdentityUtil.fakeIdentity(), "queryId", "default", + new TableName("lambda", "fake")); + metadataHandler.doGetTable(allocator, req); + } + catch (Exception ex) { + logger.info("doGetTableFail: ", ex); + throw new LambdaFunctionException(ex.getMessage(), false, "repackaged"); + } + } + + /** + * 200,000,000 million partitions pruned down to 38,000 and transmitted in 25 seconds + * + * @throws Exception + */ + @Test + public void doGetTableLayout() + throws Exception + { + logger.info("doGetTableLayout - enter"); + + Schema tableSchema = SchemaBuilder.newBuilder() + .addIntField("day") + .addIntField("month") + .addIntField("year") + .build(); + + Set partitionCols = new HashSet<>(); + partitionCols.add("day"); + partitionCols.add("month"); + partitionCols.add("year"); + + Map constraintsMap = new HashMap<>(); + + constraintsMap.put("day", SortedRangeSet.copyOf(Types.MinorType.INT.getType(), + ImmutableList.of(Range.greaterThan(allocator, Types.MinorType.INT.getType(), 20)), false)); + + constraintsMap.put("month", SortedRangeSet.copyOf(Types.MinorType.INT.getType(), + ImmutableList.of(Range.greaterThan(allocator, Types.MinorType.INT.getType(), 2)), false)); + + constraintsMap.put("year", SortedRangeSet.copyOf(Types.MinorType.INT.getType(), + ImmutableList.of(Range.greaterThan(allocator, Types.MinorType.INT.getType(), 1900)), false)); + + GetTableLayoutRequest req = null; + GetTableLayoutResponse res = null; + try { + + req = new GetTableLayoutRequest(IdentityUtil.fakeIdentity(), "queryId", "default", + new TableName("schema1", "table1"), + new Constraints(constraintsMap), + tableSchema, + partitionCols); + ObjectMapperUtil.assertSerialization(req, req.getClass()); + + res = metadataHandler.doGetTableLayout(allocator, req); + ObjectMapperUtil.assertSerialization(res, res.getClass()); + + logger.info("doGetTableLayout - {}", res); + Block partitions = res.getPartitions(); + for (int row = 0; row < partitions.getRowCount() && row < 10; row++) { + logger.info("doGetTableLayout:{} {}", row, BlockUtils.rowToString(partitions, row)); + } + assertTrue(partitions.getRowCount() > 0); + logger.info("doGetTableLayout: partitions[{}]", partitions.getRowCount()); + } + finally { + try { + req.close(); + res.close(); + } + catch (Exception ex) { + logger.error("doGetTableLayout: ", ex); + } + } + + logger.info("doGetTableLayout - exit"); + } + + /** + * The goal of this test is to test happy case for getting splits and also to exercise the continuation token + * logic specifically. + */ + @Test + public void doGetSplits() + { + logger.info("doGetSplits: enter"); + + String yearCol = "year"; + String monthCol = "month"; + String dayCol = "day"; + + //This is the schema that ExampleMetadataHandler has layed out for a 'Partition' so we need to populate this + //minimal set of info here. + Schema schema = SchemaBuilder.newBuilder() + .addField(yearCol, new ArrowType.Int(16, false)) + .addField(monthCol, new ArrowType.Int(16, false)) + .addField(dayCol, new ArrowType.Int(16, false)) + .addField(ExampleMetadataHandler.PARTITION_LOCATION, new ArrowType.Utf8()) + .addField(ExampleMetadataHandler.SERDE, new ArrowType.Utf8()) + .build(); + + List partitionCols = new ArrayList<>(); + partitionCols.add(yearCol); + partitionCols.add(monthCol); + partitionCols.add(dayCol); + + Map constraintsMap = new HashMap<>(); + + constraintsMap.put(dayCol, SortedRangeSet.copyOf(Types.MinorType.INT.getType(), + ImmutableList.of(Range.greaterThan(allocator, Types.MinorType.INT.getType(), 20)), false)); + + Block partitions = allocator.createBlock(schema); + + int num_partitions = 100; + for (int i = 0; i < num_partitions; i++) { + BlockUtils.setValue(partitions.getFieldVector(yearCol), i, 2016 + i); + BlockUtils.setValue(partitions.getFieldVector(monthCol), i, (i % 12) + 1); + BlockUtils.setValue(partitions.getFieldVector(dayCol), i, (i % 28) + 1); + BlockUtils.setValue(partitions.getFieldVector(ExampleMetadataHandler.PARTITION_LOCATION), i, String.valueOf(i)); + BlockUtils.setValue(partitions.getFieldVector(ExampleMetadataHandler.SERDE), i, "TextInputType"); + } + partitions.setRowCount(num_partitions); + + String continuationToken = null; + GetSplitsRequest originalReq = new GetSplitsRequest(IdentityUtil.fakeIdentity(), "queryId", "catalog_name", + new TableName("schema", "table_name"), + partitions, + partitionCols, + new Constraints(constraintsMap), + continuationToken); + int numContinuations = 0; + do { + GetSplitsRequest req = new GetSplitsRequest(originalReq, continuationToken); + ObjectMapperUtil.assertSerialization(req, req.getClass()); + + logger.info("doGetSplits: req[{}]", req); + metadataHandler.setEncryption(numContinuations % 2 == 0); + logger.info("doGetSplits: Toggle encryption " + (numContinuations % 2 == 0)); + + MetadataResponse rawResponse = metadataHandler.doGetSplits(allocator, req); + ObjectMapperUtil.assertSerialization(rawResponse, rawResponse.getClass()); + assertEquals(MetadataRequestType.GET_SPLITS, rawResponse.getRequestType()); + + GetSplitsResponse response = (GetSplitsResponse) rawResponse; + continuationToken = response.getContinuationToken(); + + logger.info("doGetSplits: continuationToken[{}] - numSplits[{}] - maxSplits[{}]", + new Object[] {continuationToken, response.getSplits().size(), MAX_SPLITS_PER_REQUEST}); + + for (Split nextSplit : response.getSplits()) { + if (numContinuations % 2 == 0) { + assertNotNull(nextSplit.getEncryptionKey()); + } + else { + assertNull(nextSplit.getEncryptionKey()); + } + assertNotNull(nextSplit.getProperty(SplitProperties.LOCATION.getId())); + assertNotNull(nextSplit.getProperty(SplitProperties.SERDE.getId())); + assertNotNull(nextSplit.getProperty(SplitProperties.SPLIT_PART.getId())); + } + + assertTrue("Continuation criteria violated", (response.getSplits().size() == MAX_SPLITS_PER_REQUEST && + response.getContinuationToken() != null) || response.getSplits().size() < MAX_SPLITS_PER_REQUEST); + + if (continuationToken != null) { + numContinuations++; + } + } + while (continuationToken != null); + + assertTrue(numContinuations > 0); + + logger.info("doGetSplits: exit"); + } +} diff --git a/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/examples/ExampleRecordHandlerTest.java b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/examples/ExampleRecordHandlerTest.java new file mode 100644 index 0000000000..f71a31c32e --- /dev/null +++ b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/examples/ExampleRecordHandlerTest.java @@ -0,0 +1,340 @@ +package com.amazonaws.athena.connector.lambda.examples; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.amazonaws.athena.connector.lambda.data.S3BlockSpillReader; +import com.amazonaws.athena.connector.lambda.data.BlockUtils; +import com.amazonaws.athena.connector.lambda.data.FieldBuilder; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.AllOrNoneValueSet; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.domain.predicate.EquatableValueSet; +import com.amazonaws.athena.connector.lambda.domain.predicate.Range; +import com.amazonaws.athena.connector.lambda.domain.predicate.SortedRangeSet; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import com.amazonaws.athena.connector.lambda.domain.spill.S3SpillLocation; +import com.amazonaws.athena.connector.lambda.domain.spill.SpillLocation; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsResponse; +import com.amazonaws.athena.connector.lambda.records.RecordRequest; +import com.amazonaws.athena.connector.lambda.records.RecordResponse; +import com.amazonaws.athena.connector.lambda.records.RecordService; +import com.amazonaws.athena.connector.lambda.records.RemoteReadRecordsResponse; +import com.amazonaws.athena.connector.lambda.security.EncryptionKey; +import com.amazonaws.athena.connector.lambda.security.EncryptionKeyFactory; +import com.amazonaws.athena.connector.lambda.security.IdentityUtil; +import com.amazonaws.athena.connector.lambda.security.LocalKeyFactory; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.serde.ObjectMapperUtil; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.PutObjectResult; +import com.amazonaws.services.s3.model.S3Object; +import com.amazonaws.services.s3.model.S3ObjectInputStream; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.google.common.collect.ImmutableList; +import com.google.common.io.ByteStreams; +import org.apache.arrow.vector.types.FloatingPointPrecision; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.arrow.vector.types.pojo.Schema; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ExampleRecordHandlerTest +{ + private static final Logger logger = LoggerFactory.getLogger(ExampleMetadataHandlerTest.class); + + private EncryptionKeyFactory keyFactory = new LocalKeyFactory(); + private RecordService recordService; + private List mockS3Storage = new ArrayList<>(); + private AmazonS3 amazonS3; + private AWSSecretsManager awsSecretsManager; + private AmazonAthena athena; + private S3BlockSpillReader spillReader; + private BlockAllocatorImpl allocator; + private Schema schemaForRead; + + @Before + public void setUp() + { + logger.info("setUpBefore - enter"); + + schemaForRead = SchemaBuilder.newBuilder() + .addField("col1", new ArrowType.Int(32, true)) + .addField("col2", new ArrowType.Utf8()) + .addField("col3", new ArrowType.FloatingPoint(FloatingPointPrecision.DOUBLE)) + .addField("int", Types.MinorType.INT.getType()) + .addField("tinyint", Types.MinorType.TINYINT.getType()) + .addField("smallint", Types.MinorType.SMALLINT.getType()) + .addField("bigint", Types.MinorType.BIGINT.getType()) + .addField("uint1", Types.MinorType.UINT1.getType()) + .addField("uint2", Types.MinorType.UINT2.getType()) + .addField("uint4", Types.MinorType.UINT4.getType()) + .addField("uint8", Types.MinorType.UINT8.getType()) + .addField("float4", Types.MinorType.FLOAT4.getType()) + .addField("float8", Types.MinorType.FLOAT8.getType()) + .addField("bit", Types.MinorType.BIT.getType()) + .addField("varchar", Types.MinorType.VARCHAR.getType()) + .addField("varbinary", Types.MinorType.VARBINARY.getType()) + .addField("datemilli", Types.MinorType.DATEMILLI.getType()) + .addField("dateday", Types.MinorType.DATEDAY.getType()) + .addField("decimal", new ArrowType.Decimal(10, 2)) + .addField("decimalLong", new ArrowType.Decimal(36, 2)) //Example of a List of Structs + .addField( + FieldBuilder.newBuilder("list", new ArrowType.List()) + .addField( + FieldBuilder.newBuilder("innerStruct", Types.MinorType.STRUCT.getType()) + .addStringField("varchar") + .addBigIntField("bigint") + .build()) + .build()) + //Example of a List Of Lists + .addField( + FieldBuilder.newBuilder("outerlist", new ArrowType.List()) + .addListField("innerList", Types.MinorType.VARCHAR.getType()) + .build()) + .addMetadata("partitionCols", "col1") + .build(); + + allocator = new BlockAllocatorImpl(); + + amazonS3 = mock(AmazonS3.class); + awsSecretsManager = mock(AWSSecretsManager.class); + athena = mock(AmazonAthena.class); + + when(amazonS3.putObject(anyObject(), anyObject(), anyObject(), anyObject())) + .thenAnswer(new Answer() + { + @Override + public Object answer(InvocationOnMock invocationOnMock) + throws Throwable + { + InputStream inputStream = (InputStream) invocationOnMock.getArguments()[2]; + ByteHolder byteHolder = new ByteHolder(); + byteHolder.setBytes(ByteStreams.toByteArray(inputStream)); + mockS3Storage.add(byteHolder); + return mock(PutObjectResult.class); + } + }); + + when(amazonS3.getObject(anyString(), anyString())) + .thenAnswer(new Answer() + { + @Override + public Object answer(InvocationOnMock invocationOnMock) + throws Throwable + { + S3Object mockObject = mock(S3Object.class); + ByteHolder byteHolder = mockS3Storage.get(0); + mockS3Storage.remove(0); + when(mockObject.getObjectContent()).thenReturn( + new S3ObjectInputStream( + new ByteArrayInputStream(byteHolder.getBytes()), null)); + return mockObject; + } + }); + + recordService = new LocalHandler(allocator, amazonS3, awsSecretsManager, athena); + spillReader = new S3BlockSpillReader(amazonS3, allocator); + + logger.info("setUpBefore - exit"); + } + + @After + public void after() + { + allocator.close(); + } + + @Test + public void doReadRecordsNoSpill() + { + logger.info("doReadRecordsNoSpill: enter"); + for (int i = 0; i < 2; i++) { + EncryptionKey encryptionKey = (i % 2 == 0) ? keyFactory.create() : null; + logger.info("doReadRecordsNoSpill: Using encryptionKey[" + encryptionKey + "]"); + + Map constraintsMap = new HashMap<>(); + constraintsMap.put("col3", SortedRangeSet.copyOf(Types.MinorType.FLOAT8.getType(), + ImmutableList.of(Range.equal(allocator, Types.MinorType.FLOAT8.getType(), 22.0D)), false)); + + ReadRecordsRequest request = new ReadRecordsRequest(IdentityUtil.fakeIdentity(), + "catalog", + "queryId-" + System.currentTimeMillis(), + new TableName("schema", "table"), + schemaForRead, + Split.newBuilder(makeSpillLocation(), encryptionKey).add("col1", "10").build(), + new Constraints(constraintsMap), + 100_000_000_000L, //100GB don't expect this to spill + 100_000_000_000L + ); + ObjectMapperUtil.assertSerialization(request, request.getClass()); + + RecordResponse rawResponse = recordService.readRecords(request); + ObjectMapperUtil.assertSerialization(rawResponse, rawResponse.getClass()); + + assertTrue(rawResponse instanceof ReadRecordsResponse); + + ReadRecordsResponse response = (ReadRecordsResponse) rawResponse; + logger.info("doReadRecordsNoSpill: rows[{}]", response.getRecordCount()); + + assertTrue(response.getRecords().getRowCount() == 1); + logger.info("doReadRecordsNoSpill: {}", BlockUtils.rowToString(response.getRecords(), 0)); + } + logger.info("doReadRecordsNoSpill: exit"); + } + + @Test + public void doReadRecordsSpill() + throws Exception + { + logger.info("doReadRecordsSpill: enter"); + for (int i = 0; i < 2; i++) { + EncryptionKey encryptionKey = (i % 2 == 0) ? keyFactory.create() : null; + logger.info("doReadRecordsSpill: Using encryptionKey[" + encryptionKey + "]"); + + Map constraintsMap = new HashMap<>(); + constraintsMap.put("col3", SortedRangeSet.copyOf(Types.MinorType.FLOAT8.getType(), + ImmutableList.of(Range.greaterThan(allocator, Types.MinorType.FLOAT8.getType(), -10000D)), false)); + constraintsMap.put("unknown", EquatableValueSet.newBuilder(allocator, Types.MinorType.FLOAT8.getType(), false, true).add(1.1D).build()); + constraintsMap.put("unknown2", new AllOrNoneValueSet(Types.MinorType.FLOAT8.getType(), false, true)); + + ReadRecordsRequest request = new ReadRecordsRequest(IdentityUtil.fakeIdentity(), + "catalog", + "queryId-" + System.currentTimeMillis(), + new TableName("schema", "table"), + schemaForRead, + Split.newBuilder(makeSpillLocation(), encryptionKey).add("col1", "1").build(), + new Constraints(constraintsMap), + 1_600_000L, //~1.5MB so we should see some spill + 1000L + ); + ObjectMapperUtil.assertSerialization(request, request.getClass()); + + RecordResponse rawResponse = recordService.readRecords(request); + ObjectMapperUtil.assertSerialization(rawResponse, rawResponse.getClass()); + + assertTrue(rawResponse instanceof RemoteReadRecordsResponse); + + try (RemoteReadRecordsResponse response = (RemoteReadRecordsResponse) rawResponse) { + logger.info("doReadRecordsSpill: remoteBlocks[{}]", response.getRemoteBlocks().size()); + + assertTrue(response.getNumberBlocks() > 1); + + int blockNum = 0; + for (SpillLocation next : response.getRemoteBlocks()) { + S3SpillLocation spillLocation = (S3SpillLocation) next; + try (Block block = spillReader.read(spillLocation, response.getEncryptionKey(), response.getSchema())) { + + logger.info("doReadRecordsSpill: blockNum[{}] and recordCount[{}]", blockNum++, block.getRowCount()); + // assertTrue(++blockNum < response.getRemoteBlocks().size() && block.getRowCount() > 10_000); + + logger.info("doReadRecordsSpill: {}", BlockUtils.rowToString(block, 0)); + assertNotNull(BlockUtils.rowToString(block, 0)); + } + } + } + } + logger.info("doReadRecordsSpill: exit"); + } + + private static class LocalHandler + implements RecordService + { + private ExampleRecordHandler handler; + private final BlockAllocatorImpl allocator; + + public LocalHandler(BlockAllocatorImpl allocator, AmazonS3 amazonS3, AWSSecretsManager secretsManager, AmazonAthena athena) + { + handler = new ExampleRecordHandler(amazonS3, secretsManager, athena); + handler.setNumRows(20_000);//lower number for faster unit tests vs integ tests + this.allocator = allocator; + } + + @Override + public RecordResponse readRecords(RecordRequest request) + { + + try { + switch (request.getRequestType()) { + case READ_RECORDS: + ReadRecordsRequest req = (ReadRecordsRequest) request; + RecordResponse response = handler.doReadRecords(allocator, req); + return response; + default: + throw new RuntimeException("Unknown request type " + request.getRequestType()); + } + } + catch (Exception ex) { + throw new RuntimeException(ex); + } + } + } + + private SpillLocation makeSpillLocation() + { + return S3SpillLocation.newBuilder() + .withBucket("athena-virtuoso-test") + .withPrefix("lambda-spill") + .withQueryId(UUID.randomUUID().toString()) + .withSplitId(UUID.randomUUID().toString()) + .withIsDirectory(true) + .build(); + } + + private class ByteHolder + { + private byte[] bytes; + + public void setBytes(byte[] bytes) + { + this.bytes = bytes; + } + + public byte[] getBytes() + { + return bytes; + } + } +} diff --git a/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/handlers/CompositeHandlerTest.java b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/handlers/CompositeHandlerTest.java new file mode 100644 index 0000000000..001bbf1e9c --- /dev/null +++ b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/handlers/CompositeHandlerTest.java @@ -0,0 +1,229 @@ +package com.amazonaws.athena.connector.lambda.handlers; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.amazonaws.athena.connector.lambda.data.BlockUtils; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.domain.spill.S3SpillLocation; +import com.amazonaws.athena.connector.lambda.examples.ExampleMetadataHandlerTest; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesResponse; +import com.amazonaws.athena.connector.lambda.metadata.MetadataRequestType; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsResponse; +import com.amazonaws.athena.connector.lambda.request.PingRequest; +import com.amazonaws.athena.connector.lambda.request.PingResponse; +import com.amazonaws.athena.connector.lambda.security.IdentityUtil; +import com.amazonaws.athena.connector.lambda.serde.ObjectMapperFactory; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.arrow.vector.types.pojo.Schema; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayOutputStream; +import java.util.Collections; +import java.util.HashMap; +import java.util.UUID; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class CompositeHandlerTest +{ + private static final Logger logger = LoggerFactory.getLogger(ExampleMetadataHandlerTest.class); + + private MetadataHandler mockMetadataHandler; + private RecordHandler mockRecordHandler; + private CompositeHandler compositeHandler; + private BlockAllocatorImpl allocator; + private ObjectMapper objectMapper; + private Schema schemaForRead; + + @Before + public void setUp() + throws Exception + { + allocator = new BlockAllocatorImpl(); + objectMapper = ObjectMapperFactory.create(allocator); + mockMetadataHandler = mock(MetadataHandler.class); + mockRecordHandler = mock(RecordHandler.class); + + schemaForRead = SchemaBuilder.newBuilder() + .addField("col1", new ArrowType.Int(32, true)) + .build(); + + when(mockMetadataHandler.doGetTableLayout(any(BlockAllocatorImpl.class), any(GetTableLayoutRequest.class))) + .thenReturn(new GetTableLayoutResponse("catalog", + new TableName("schema", "table"), + BlockUtils.newBlock(allocator, "col1", Types.MinorType.BIGINT.getType(), 1L))); + + when(mockMetadataHandler.doListTables(any(BlockAllocatorImpl.class), any(ListTablesRequest.class))) + .thenReturn(new ListTablesResponse("catalog", + Collections.singletonList(new TableName("schema", "table")))); + + when(mockMetadataHandler.doGetTable(any(BlockAllocatorImpl.class), any(GetTableRequest.class))) + .thenReturn(new GetTableResponse("catalog", + new TableName("schema", "table"), + SchemaBuilder.newBuilder().addStringField("col1").build())); + + when(mockMetadataHandler.doListSchemaNames(any(BlockAllocatorImpl.class), any(ListSchemasRequest.class))) + .thenReturn(new ListSchemasResponse("catalog", Collections.singleton("schema1"))); + + when(mockMetadataHandler.doGetSplits(any(BlockAllocatorImpl.class), any(GetSplitsRequest.class))) + .thenReturn(new GetSplitsResponse("catalog", Split.newBuilder(null, null).build())); + + when(mockMetadataHandler.doPing(any(PingRequest.class))) + .thenReturn(new PingResponse("catalog", "queryId", "type", 23)); + + when(mockRecordHandler.doReadRecords(any(BlockAllocatorImpl.class), any(ReadRecordsRequest.class))) + .thenReturn(new ReadRecordsResponse("catalog", + BlockUtils.newEmptyBlock(allocator, "col", new ArrowType.Int(32, true)))); + + compositeHandler = new CompositeHandler(mockMetadataHandler, mockRecordHandler); + } + + @After + public void after() + { + allocator.close(); + } + + @Test + public void doReadRecords() + throws Exception + { + logger.info("doReadRecords - enter"); + ReadRecordsRequest req = new ReadRecordsRequest(IdentityUtil.fakeIdentity(), + "catalog", + "queryId-" + System.currentTimeMillis(), + new TableName("schema", "table"), + schemaForRead, + Split.newBuilder(S3SpillLocation.newBuilder() + .withBucket("athena-virtuoso-test") + .withPrefix("lambda-spill") + .withQueryId(UUID.randomUUID().toString()) + .withSplitId(UUID.randomUUID().toString()) + .withIsDirectory(true) + .build(), null).build(), + new Constraints(new HashMap<>()), + 100_000_000_000L, //100GB don't expect this to spill + 100_000_000_000L + ); + compositeHandler.handleRequest(allocator, req, new ByteArrayOutputStream(), objectMapper); + verify(mockRecordHandler, times(1)) + .doReadRecords(any(BlockAllocator.class), any(ReadRecordsRequest.class)); + logger.info("readRecords - exit"); + } + + @Test + public void doListSchemaNames() + throws Exception + { + logger.info("doListSchemas - enter"); + ListSchemasRequest req = mock(ListSchemasRequest.class); + when(req.getRequestType()).thenReturn(MetadataRequestType.LIST_SCHEMAS); + compositeHandler.handleRequest(allocator, req, new ByteArrayOutputStream(), objectMapper); + verify(mockMetadataHandler, times(1)).doListSchemaNames(any(BlockAllocatorImpl.class), any(ListSchemasRequest.class)); + logger.info("doListSchemas - exit"); + } + + @Test + public void doListTables() + throws Exception + { + logger.info("doListTables - enter"); + ListTablesRequest req = mock(ListTablesRequest.class); + when(req.getRequestType()).thenReturn(MetadataRequestType.LIST_TABLES); + compositeHandler.handleRequest(allocator, req, new ByteArrayOutputStream(), objectMapper); + verify(mockMetadataHandler, times(1)).doListTables(any(BlockAllocatorImpl.class), any(ListTablesRequest.class)); + logger.info("doListTables - exit"); + } + + @Test + public void doGetTable() + throws Exception + { + logger.info("doGetTable - enter"); + GetTableRequest req = mock(GetTableRequest.class); + when(req.getRequestType()).thenReturn(MetadataRequestType.GET_TABLE); + compositeHandler.handleRequest(allocator, req, new ByteArrayOutputStream(), objectMapper); + verify(mockMetadataHandler, times(1)).doGetTable(any(BlockAllocatorImpl.class), any(GetTableRequest.class)); + logger.info("doGetTable - exit"); + } + + @Test + public void doGetTableLayout() + throws Exception + { + logger.info("doGetTableLayout - enter"); + GetTableLayoutRequest req = mock(GetTableLayoutRequest.class); + when(req.getRequestType()).thenReturn(MetadataRequestType.GET_TABLE_LAYOUT); + compositeHandler.handleRequest(allocator, req, new ByteArrayOutputStream(), objectMapper); + verify(mockMetadataHandler, times(1)).doGetTableLayout(any(BlockAllocatorImpl.class), any(GetTableLayoutRequest.class)); + logger.info("doGetTableLayout - exit"); + } + + @Test + public void doGetSplits() + throws Exception + { + logger.info("doGetSplits - enter"); + GetSplitsRequest req = mock(GetSplitsRequest.class); + when(req.getRequestType()).thenReturn(MetadataRequestType.GET_SPLITS); + compositeHandler.handleRequest(allocator, req, new ByteArrayOutputStream(), objectMapper); + verify(mockMetadataHandler, times(1)).doGetSplits(any(BlockAllocatorImpl.class), any(GetSplitsRequest.class)); + logger.info("doGetSplits - exit"); + } + + @Test + public void doPing() + throws Exception + { + logger.info("doPing - enter"); + PingRequest req = mock(PingRequest.class); + when(req.getCatalogName()).thenReturn("catalog"); + when(req.getQueryId()).thenReturn("queryId"); + compositeHandler.handleRequest(allocator, req, new ByteArrayOutputStream(), objectMapper); + verify(mockMetadataHandler, times(1)).doPing(any(PingRequest.class)); + logger.info("doPing - exit"); + } +} diff --git a/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/handlers/GlueMetadataHandlerTest.java b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/handlers/GlueMetadataHandlerTest.java new file mode 100644 index 0000000000..7f0d0e7d47 --- /dev/null +++ b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/handlers/GlueMetadataHandlerTest.java @@ -0,0 +1,285 @@ +package com.amazonaws.athena.connector.lambda.handlers; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.amazonaws.athena.connector.lambda.data.BlockWriter; +import com.amazonaws.athena.connector.lambda.data.FieldBuilder; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesResponse; +import com.amazonaws.athena.connector.lambda.security.IdentityUtil; +import com.amazonaws.athena.connector.lambda.security.LocalKeyFactory; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.glue.AWSGlue; +import com.amazonaws.services.glue.model.Column; +import com.amazonaws.services.glue.model.Database; +import com.amazonaws.services.glue.model.GetDatabasesRequest; +import com.amazonaws.services.glue.model.GetDatabasesResult; +import com.amazonaws.services.glue.model.GetTableResult; +import com.amazonaws.services.glue.model.GetTablesRequest; +import com.amazonaws.services.glue.model.GetTablesResult; +import com.amazonaws.services.glue.model.StorageDescriptor; +import com.amazonaws.services.glue.model.Table; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Field; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.runners.MockitoJUnitRunner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class GlueMetadataHandlerTest +{ + private static final Logger logger = LoggerFactory.getLogger(MockitoJUnitRunner.class); + + private String accountId = IdentityUtil.fakeIdentity().getAccount(); + private String queryId = "queryId"; + private String catalog = "default"; + private String schema = "database1"; + private String table = "table1"; + + private GlueMetadataHandler handler; + + private BlockAllocatorImpl allocator; + + @Mock + private AWSGlue mockGlue; + + @Before + public void setUp() + throws Exception + { + handler = new GlueMetadataHandler(mockGlue, + new LocalKeyFactory(), + mock(AWSSecretsManager.class), + mock(AmazonAthena.class), + "glue-test", + "spill-bucket", + "spill-prefix") + { + @Override + public GetTableLayoutResponse doGetTableLayout(BlockAllocator blockAllocator, GetTableLayoutRequest request) + { + throw new UnsupportedOperationException(); + } + + @Override + public void getPartitions(BlockWriter blockWriter, GetTableLayoutRequest request, QueryStatusChecker queryStatusChecker) + throws Exception + { + throw new UnsupportedOperationException(); + } + + @Override + public GetSplitsResponse doGetSplits(BlockAllocator blockAllocator, GetSplitsRequest request) + { + throw new UnsupportedOperationException(); + } + + @Override + protected Field convertField(String name, String type) + { + if ("int".equals(type)) { + return FieldBuilder.newBuilder(name, Types.MinorType.INT.getType()).build(); + } + else if ("bigint".equals(type)) { + return FieldBuilder.newBuilder(name, Types.MinorType.BIGINT.getType()).build(); + } + else if ("string".equals(type)) { + return FieldBuilder.newBuilder(name, Types.MinorType.VARCHAR.getType()).build(); + } + throw new IllegalArgumentException("Unsupported type " + type); + } + }; + allocator = new BlockAllocatorImpl(); + } + + @After + public void tearDown() + throws Exception + { + allocator.close(); + } + + @Test + public void doListSchemaNames() + throws Exception + { + logger.info("doListSchemaNames: enter"); + + List databases = new ArrayList<>(); + databases.add(new Database().withName("db1")); + databases.add(new Database().withName("db2")); + + when(mockGlue.getDatabases(any(GetDatabasesRequest.class))) + .thenAnswer((InvocationOnMock invocationOnMock) -> + { + GetDatabasesRequest request = (GetDatabasesRequest) invocationOnMock.getArguments()[0]; + assertEquals(accountId, request.getCatalogId()); + GetDatabasesResult mockResult = mock(GetDatabasesResult.class); + if (request.getNextToken() == null) { + when(mockResult.getDatabaseList()).thenReturn(databases); + when(mockResult.getNextToken()).thenReturn("next"); + } + else { + //only return real info on 1st call + when(mockResult.getDatabaseList()).thenReturn(new ArrayList<>()); + when(mockResult.getNextToken()).thenReturn(null); + } + return mockResult; + }); + + ListSchemasRequest req = new ListSchemasRequest(IdentityUtil.fakeIdentity(), queryId, catalog); + ListSchemasResponse res = handler.doListSchemaNames(allocator, req); + + logger.info("doListSchemas - {}", res.getSchemas()); + + assertEquals(databases.stream().map(next -> next.getName()).collect(Collectors.toList()), + new ArrayList<>(res.getSchemas())); + + verify(mockGlue, times(2)).getDatabases(any(GetDatabasesRequest.class)); + logger.info("doListSchemaNames: exit"); + } + + @Test + public void doListTables() + throws Exception + { + logger.info("doListTables - enter"); + + List

tables = new ArrayList<>(); + tables.add(new Table().withName("table1")); + tables.add(new Table().withName("table2")); + + when(mockGlue.getTables(any(GetTablesRequest.class))) + .thenAnswer((InvocationOnMock invocationOnMock) -> + { + GetTablesRequest request = (GetTablesRequest) invocationOnMock.getArguments()[0]; + assertEquals(accountId, request.getCatalogId()); + assertEquals(schema, request.getDatabaseName()); + GetTablesResult mockResult = mock(GetTablesResult.class); + if (request.getNextToken() == null) { + when(mockResult.getTableList()).thenReturn(tables); + when(mockResult.getNextToken()).thenReturn("next"); + } + else { + //only return real info on 1st call + when(mockResult.getTableList()).thenReturn(new ArrayList<>()); + when(mockResult.getNextToken()).thenReturn(null); + } + return mockResult; + }); + + ListTablesRequest req = new ListTablesRequest(IdentityUtil.fakeIdentity(), queryId, catalog, schema); + ListTablesResponse res = handler.doListTables(allocator, req); + logger.info("doListTables - {}", res.getTables()); + + Set tableNames = tables.stream().map(next -> next.getName()).collect(Collectors.toSet()); + for (TableName next : res.getTables()) { + assertEquals(schema, next.getSchemaName()); + assertTrue(tableNames.contains(next.getTableName())); + } + assertEquals(tableNames.size(), res.getTables().size()); + + logger.info("doListTables - exit"); + } + + @Test + public void doGetTable() + throws Exception + { + logger.info("doGetTable - enter"); + + Map expectedParams = new HashMap<>(); + expectedParams.put("param1", "val1"); + expectedParams.put("param2", "val2"); + + List columns = new ArrayList<>(); + columns.add(new Column().withName("col1").withType("int").withComment("comment")); + columns.add(new Column().withName("col2").withType("bigint").withComment("comment")); + columns.add(new Column().withName("col3").withType("string").withComment("comment")); + + Table mockTable = mock(Table.class); + StorageDescriptor mockSd = mock(StorageDescriptor.class); + + when(mockTable.getName()).thenReturn(table); + when(mockTable.getStorageDescriptor()).thenReturn(mockSd); + when(mockTable.getParameters()).thenReturn(expectedParams); + when(mockSd.getColumns()).thenReturn(columns); + + when(mockGlue.getTable(any(com.amazonaws.services.glue.model.GetTableRequest.class))) + .thenAnswer((InvocationOnMock invocationOnMock) -> + { + com.amazonaws.services.glue.model.GetTableRequest request = + (com.amazonaws.services.glue.model.GetTableRequest) invocationOnMock.getArguments()[0]; + + assertEquals(accountId, request.getCatalogId()); + assertEquals(schema, request.getDatabaseName()); + assertEquals(table, request.getName()); + + GetTableResult mockResult = mock(GetTableResult.class); + when(mockResult.getTable()).thenReturn(mockTable); + return mockResult; + }); + + GetTableRequest req = new GetTableRequest(IdentityUtil.fakeIdentity(), queryId, catalog, new TableName(schema, table)); + GetTableResponse res = handler.doGetTable(allocator, req); + + logger.info("doGetTable - {}", res); + + assertTrue(res.getSchema().getFields().size() > 0); + assertTrue(res.getSchema().getCustomMetadata().size() > 0); + + logger.info("doGetTable - exit"); + } +} diff --git a/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/metadata/glue/GlueFieldLexerTest.java b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/metadata/glue/GlueFieldLexerTest.java new file mode 100644 index 0000000000..7ac78b65c5 --- /dev/null +++ b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/metadata/glue/GlueFieldLexerTest.java @@ -0,0 +1,108 @@ +package com.amazonaws.athena.connector.lambda.metadata.glue; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Field; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +import static org.junit.Assert.*; + +public class GlueFieldLexerTest +{ + private static final Logger logger = LoggerFactory.getLogger(GlueFieldLexerTest.class); + + private static final String INPUT1 = "STRUCT < street_address: STRUCT < street_number: INT, street_name: STRING, street_type: STRING >, country: STRING, postal_code: ARRAY>"; + + private static final String INPUT2 = "ARRAY"; + + private static final String INPUT3 = "INT"; + + @Test + public void basicLexTest() + { + logger.info("basicLexTest: enter"); + + Field field = GlueFieldLexer.lex("testField", INPUT2); + assertEquals("testField", field.getName()); + assertEquals(Types.MinorType.LIST, Types.getMinorTypeForArrowType(field.getType())); + assertEquals(Types.MinorType.VARCHAR, Types.getMinorTypeForArrowType(field.getChildren().get(0).getType())); + + logger.info("basicLexTest: exit"); + } + + @Test + public void baseLexTest() + { + logger.info("baseLexTest: enter"); + + Field field = GlueFieldLexer.lex("testField", INPUT3); + assertEquals("testField", field.getName()); + assertEquals(Types.MinorType.INT, Types.getMinorTypeForArrowType(field.getType())); + assertEquals(0, field.getChildren().size()); + + logger.info("baseLexTest: exit"); + } + + @Test + public void lexTest() + { + logger.info("lexTest: enter"); + + Field field = GlueFieldLexer.lex("testField", INPUT1); + + logger.info("lexTest: {}", field); + assertEquals("testField", field.getName()); + assertEquals(Types.MinorType.STRUCT, Types.getMinorTypeForArrowType(field.getType())); + assertEquals(3, field.getChildren().size()); + + List level1 = field.getChildren(); + assertEquals("street_address", level1.get(0).getName()); + assertEquals(Types.MinorType.STRUCT, Types.getMinorTypeForArrowType(level1.get(0).getType())); + assertEquals(3, level1.get(0).getChildren().size()); + + List level2 = level1.get(0).getChildren(); + assertEquals("street_number", level2.get(0).getName()); + assertEquals(Types.MinorType.INT, Types.getMinorTypeForArrowType(level2.get(0).getType())); + assertEquals(0, level2.get(0).getChildren().size()); + assertEquals("street_name", level2.get(1).getName()); + assertEquals(Types.MinorType.VARCHAR, Types.getMinorTypeForArrowType(level2.get(1).getType())); + assertEquals(0, level2.get(1).getChildren().size()); + assertEquals("street_type", level2.get(2).getName()); + assertEquals(Types.MinorType.VARCHAR, Types.getMinorTypeForArrowType(level2.get(2).getType())); + assertEquals(0, level2.get(2).getChildren().size()); + + assertEquals("country", level1.get(1).getName()); + assertEquals(Types.MinorType.VARCHAR, Types.getMinorTypeForArrowType(level1.get(1).getType())); + assertEquals(0, level1.get(1).getChildren().size()); + + assertEquals("postal_code", level1.get(2).getName()); + assertEquals(Types.MinorType.LIST, Types.getMinorTypeForArrowType(level1.get(2).getType())); + assertEquals(1, level1.get(2).getChildren().size()); + assertEquals(Types.MinorType.VARCHAR, Types.getMinorTypeForArrowType(level1.get(2).getChildren().get(0).getType())); + + logger.info("lexTest: exit"); + } +} diff --git a/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/metadata/glue/GlueTypeParserTest.java b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/metadata/glue/GlueTypeParserTest.java new file mode 100644 index 0000000000..39e2ceb972 --- /dev/null +++ b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/metadata/glue/GlueTypeParserTest.java @@ -0,0 +1,73 @@ +package com.amazonaws.athena.connector.lambda.metadata.glue; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.*; + +public class GlueTypeParserTest +{ + private static final Logger logger = LoggerFactory.getLogger(GlueTypeParserTest.class); + + private static final String INPUT = "STRUCT < street_address: STRUCT < street_number: INT, street_name: STRING, street_type: STRING >, country: STRING, postal_code: ARRAY>"; + + private static final List expectedTokens = new ArrayList<>(); + + static { + expectedTokens.add(new GlueTypeParser.Token("STRUCT", GlueTypeParser.FIELD_START, 8)); + expectedTokens.add(new GlueTypeParser.Token("street_address", GlueTypeParser.FIELD_DIV, 25)); + expectedTokens.add(new GlueTypeParser.Token("STRUCT", GlueTypeParser.FIELD_START, 34)); + expectedTokens.add(new GlueTypeParser.Token("street_number", GlueTypeParser.FIELD_DIV, 52)); + expectedTokens.add(new GlueTypeParser.Token("INT", GlueTypeParser.FIELD_SEP, 57)); + expectedTokens.add(new GlueTypeParser.Token("street_name", GlueTypeParser.FIELD_DIV, 73)); + expectedTokens.add(new GlueTypeParser.Token("STRING", GlueTypeParser.FIELD_SEP, 81)); + expectedTokens.add(new GlueTypeParser.Token("street_type", GlueTypeParser.FIELD_DIV, 97)); + expectedTokens.add(new GlueTypeParser.Token("STRING", GlueTypeParser.FIELD_END, 107)); + expectedTokens.add(new GlueTypeParser.Token("", GlueTypeParser.FIELD_SEP, 108)); + expectedTokens.add(new GlueTypeParser.Token("country", GlueTypeParser.FIELD_DIV, 118)); + expectedTokens.add(new GlueTypeParser.Token("STRING", GlueTypeParser.FIELD_SEP, 126)); + expectedTokens.add(new GlueTypeParser.Token("postal_code", GlueTypeParser.FIELD_DIV, 140)); + expectedTokens.add(new GlueTypeParser.Token("ARRAY", GlueTypeParser.FIELD_START, 147)); + expectedTokens.add(new GlueTypeParser.Token("STRING", GlueTypeParser.FIELD_END, 154)); + expectedTokens.add(new GlueTypeParser.Token("", GlueTypeParser.FIELD_END, 155)); + } + + private GlueTypeParser parser = new GlueTypeParser(INPUT); + + @Test + public void parseTest() + { + logger.info("parseTest: enter"); + int pos = 0; + while (parser.hasNext()) { + GlueTypeParser.Token next = parser.next(); + logger.info("parseTest: {} => {}", next.getValue(), next.getMarker()); + assertEquals(expectedTokens.get(pos++), next); + } + logger.info("parseTest: exits"); + } +} diff --git a/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/security/BlockCryptoTest.java b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/security/BlockCryptoTest.java new file mode 100644 index 0000000000..9f078be045 --- /dev/null +++ b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/security/BlockCryptoTest.java @@ -0,0 +1,74 @@ +package com.amazonaws.athena.connector.lambda.security; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.amazonaws.athena.connector.lambda.data.BlockUtils; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.arrow.vector.types.pojo.Schema; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class BlockCryptoTest +{ + private final EncryptionKeyFactory keyFactory = new LocalKeyFactory(); + private BlockAllocatorImpl allocator; + + @Before + public void setup() + { + allocator = new BlockAllocatorImpl(); + } + + @After + public void tearDown() + { + allocator.close(); + } + + @Test + public void test() + { + Schema schema = SchemaBuilder.newBuilder() + .addField("col1", new ArrowType.Int(32, true)) + .addField("col2", new ArrowType.Utf8()) + .build(); + + Block expected = allocator.createBlock(schema); + BlockUtils.setValue(expected.getFieldVector("col1"), 1, 100); + BlockUtils.setValue(expected.getFieldVector("col2"), 1, "VarChar"); + BlockUtils.setValue(expected.getFieldVector("col1"), 1, 101); + BlockUtils.setValue(expected.getFieldVector("col2"), 1, "VarChar1"); + expected.setRowCount(2); + + AesGcmBlockCrypto crypto = new AesGcmBlockCrypto(new BlockAllocatorImpl()); + EncryptionKey key = keyFactory.create(); + + byte[] cypher = crypto.encrypt(key, expected); + Block actual = crypto.decrypt(key, cypher, schema); + assertEquals(expected, actual); + } +} diff --git a/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/security/CacheableSecretsManagerTest.java b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/security/CacheableSecretsManagerTest.java new file mode 100644 index 0000000000..d1b719ccf1 --- /dev/null +++ b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/security/CacheableSecretsManagerTest.java @@ -0,0 +1,134 @@ +package com.amazonaws.athena.connector.lambda.security; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.amazonaws.services.secretsmanager.model.GetSecretValueRequest; +import com.amazonaws.services.secretsmanager.model.GetSecretValueResult; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +public class CacheableSecretsManagerTest +{ + private AWSSecretsManager mockSecretsManager; + + private CachableSecretsManager cachableSecretsManager; + + @Before + public void setup() + { + mockSecretsManager = mock(AWSSecretsManager.class); + cachableSecretsManager = new CachableSecretsManager(mockSecretsManager); + } + + @After + public void after() + { + reset(mockSecretsManager); + } + + @Test + public void expirationTest() + { + cachableSecretsManager.addCacheEntry("test", "value", System.currentTimeMillis()); + assertEquals("value", cachableSecretsManager.getSecret("test")); + verifyNoMoreInteractions(mockSecretsManager); + reset(mockSecretsManager); + + when(mockSecretsManager.getSecretValue(any(GetSecretValueRequest.class))) + .thenAnswer((InvocationOnMock invocation) -> { + GetSecretValueRequest request = invocation.getArgumentAt(0, GetSecretValueRequest.class); + if (request.getSecretId().equalsIgnoreCase("test")) { + return new GetSecretValueResult().withSecretString("value2"); + } + throw new RuntimeException(); + }); + + cachableSecretsManager.addCacheEntry("test", "value", 0); + assertEquals("value2", cachableSecretsManager.getSecret("test")); + } + + @Test + public void evictionTest() + { + for (int i = 0; i < CachableSecretsManager.MAX_CACHE_SIZE; i++) { + cachableSecretsManager.addCacheEntry("test" + i, "value" + i, System.currentTimeMillis()); + } + when(mockSecretsManager.getSecretValue(any(GetSecretValueRequest.class))) + .thenAnswer((InvocationOnMock invocation) -> { + GetSecretValueRequest request = invocation.getArgumentAt(0, GetSecretValueRequest.class); + return new GetSecretValueResult().withSecretString(request.getSecretId() + "_value"); + }); + + assertEquals("test_value", cachableSecretsManager.getSecret("test")); + assertEquals("test0_value", cachableSecretsManager.getSecret("test0")); + + verify(mockSecretsManager, times(2)).getSecretValue(any(GetSecretValueRequest.class)); + } + + @Test + public void resolveSecrets() + { + when(mockSecretsManager.getSecretValue(any(GetSecretValueRequest.class))) + .thenAnswer((InvocationOnMock invocation) -> { + GetSecretValueRequest request = invocation.getArgumentAt(0, GetSecretValueRequest.class); + String result = request.getSecretId(); + if (result.equalsIgnoreCase("unknown")) { + throw new RuntimeException("Unknown secret!"); + } + return new GetSecretValueResult().withSecretString(result); + }); + + String oneSecret = "${OneSecret}"; + String oneExpected = "OneSecret"; + assertEquals(oneExpected, cachableSecretsManager.resolveSecrets(oneSecret)); + + String twoSecrets = "ThisIsMyStringWith${TwoSecret}SuperSecret${Secrets}"; + String twoExpected = "ThisIsMyStringWithTwoSecretSuperSecretSecrets"; + assertEquals(twoExpected, cachableSecretsManager.resolveSecrets(twoSecrets)); + + String noSecrets = "ThisIsMyStringWithTwoSecretSuperSecretSecrets"; + String noSecretsExpected = "ThisIsMyStringWithTwoSecretSuperSecretSecrets"; + assertEquals(noSecretsExpected, cachableSecretsManager.resolveSecrets(noSecrets)); + + String commonErrors = "ThisIsM}yStringWi${thTwoSecretS{uperSecretSecrets"; + String commonErrorsExpected = "ThisIsM}yStringWi${thTwoSecretS{uperSecretSecrets"; + assertEquals(commonErrorsExpected, cachableSecretsManager.resolveSecrets(commonErrors)); + + String unknownSecret = "This${Unknown}"; + try { + cachableSecretsManager.resolveSecrets(unknownSecret); + fail("Should not see this!"); + } + catch (RuntimeException ex) {} + } +} diff --git a/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/security/IdentityUtil.java b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/security/IdentityUtil.java new file mode 100644 index 0000000000..baab50d562 --- /dev/null +++ b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/security/IdentityUtil.java @@ -0,0 +1,31 @@ +package com.amazonaws.athena.connector.lambda.security; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +public class IdentityUtil +{ + private IdentityUtil() {} + + public static FederatedIdentity fakeIdentity() + { + return new FederatedIdentity("access_key_id", "principle", "account"); + } +} diff --git a/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/serde/BlockSerializationTest.java b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/serde/BlockSerializationTest.java new file mode 100644 index 0000000000..1af6037464 --- /dev/null +++ b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/serde/BlockSerializationTest.java @@ -0,0 +1,83 @@ +package com.amazonaws.athena.connector.lambda.serde; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.amazonaws.athena.connector.lambda.data.BlockUtils; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.arrow.vector.types.Types; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import static org.junit.Assert.assertEquals; + +public class BlockSerializationTest +{ + private static final Logger logger = LoggerFactory.getLogger(BlockSerializationTest.class); + + //We use two allocators to test moving data across allocators, some operations only fail when done across. + //because of how Arrow does zero copy buffer reuse. + private BlockAllocatorImpl allocator; + private BlockAllocatorImpl otherAllocator; + private ObjectMapper objectMapper; + + @Before + public void setup() + { + otherAllocator = new BlockAllocatorImpl(); + allocator = new BlockAllocatorImpl(); + objectMapper = ObjectMapperFactory.create(allocator); + } + + @After + public void tearDown() + { + otherAllocator.close(); + allocator.close(); + } + + @Test + public void serializationTest() + throws IOException + { + logger.info("serializationTest - enter"); + + Block expected = BlockUtils.newBlock(otherAllocator, "col1", Types.MinorType.INT.getType(), 21); + + ObjectMapper serializer = ObjectMapperFactory.create(new BlockAllocatorImpl()); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.writeValue(out, expected); + + Block actual = serializer.readValue(new ByteArrayInputStream(out.toByteArray()), Block.class); + + assertEquals(expected, actual); + + logger.info("serializationTest - exit"); + } +} diff --git a/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/serde/ConstraintSerializationTest.java b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/serde/ConstraintSerializationTest.java new file mode 100644 index 0000000000..a3d1bd096e --- /dev/null +++ b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/serde/ConstraintSerializationTest.java @@ -0,0 +1,96 @@ +package com.amazonaws.athena.connector.lambda.serde; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.domain.predicate.Range; +import com.amazonaws.athena.connector.lambda.domain.predicate.SortedRangeSet; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutRequest; +import com.amazonaws.athena.connector.lambda.security.IdentityUtil; +import com.google.common.collect.ImmutableList; +import org.apache.arrow.vector.types.Types; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; + +public class ConstraintSerializationTest +{ + private static final Logger logger = LoggerFactory.getLogger(ConstraintSerializationTest.class); + + private BlockAllocatorImpl allocator; + + @Before + public void setup() + { + allocator = new BlockAllocatorImpl(); + } + + @After + public void tearDown() + { + allocator.close(); + } + + @Test + public void serializationTest() + throws Exception + { + logger.info("serializationTest - enter"); + + Map constraintsMap = new HashMap<>(); + constraintsMap.put("col2", SortedRangeSet.copyOf(Types.MinorType.BIGINT.getType(), + ImmutableList.of(Range.greaterThan(allocator, Types.MinorType.BIGINT.getType(), 950L)), false)); + + constraintsMap.put("col3", SortedRangeSet.copyOf(Types.MinorType.BIT.getType(), + ImmutableList.of(Range.equal(allocator, Types.MinorType.BIT.getType(), false)), false)); + + constraintsMap.put("col4", SortedRangeSet.copyOf(Types.MinorType.FLOAT8.getType(), + ImmutableList.of(Range.greaterThan(allocator, Types.MinorType.FLOAT8.getType(), 950.0D)), false)); + + constraintsMap.put("col5", SortedRangeSet.copyOf(Types.MinorType.VARCHAR.getType(), + ImmutableList.of(Range.equal(allocator, Types.MinorType.VARCHAR.getType(), "8"), + Range.equal(allocator, Types.MinorType.VARCHAR.getType(), "9")), false)); + + try ( + GetTableLayoutRequest req = new GetTableLayoutRequest(IdentityUtil.fakeIdentity(), + "queryId", + "default", + new TableName("schema1", "table1"), + new Constraints(constraintsMap), + SchemaBuilder.newBuilder().build(), + new HashSet<>()) + ) { + ObjectMapperUtil.assertSerialization(req, req.getClass()); + } + + logger.info("serializationTest - exit"); + } +} diff --git a/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/serde/MarkerSerializationTest.java b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/serde/MarkerSerializationTest.java new file mode 100644 index 0000000000..300623e6d9 --- /dev/null +++ b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/serde/MarkerSerializationTest.java @@ -0,0 +1,107 @@ +package com.amazonaws.athena.connector.lambda.serde; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.amazonaws.athena.connector.lambda.domain.predicate.Marker; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.arrow.vector.types.Types; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import static org.junit.Assert.*; + +public class MarkerSerializationTest +{ + private static final Logger logger = LoggerFactory.getLogger(MarkerSerializationTest.class); + private BlockAllocatorImpl allocator; + + @Before + public void setup() + { + allocator = new BlockAllocatorImpl(); + } + + @After + public void tearDown() + { + allocator.close(); + } + + @Test + public void serializationTest() + throws IOException + { + logger.info("serializationTest - enter"); + + ObjectMapper serializer = ObjectMapperFactory.create(new BlockAllocatorImpl()); + + int expectedValue = 1024; + Marker expectedMarker = Marker.exactly(allocator, Types.MinorType.INT.getType(), expectedValue); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.writeValue(out, expectedMarker); + + ObjectMapper deserializer = ObjectMapperFactory.create(allocator); + + Marker actualMarker = deserializer.readValue(new ByteArrayInputStream(out.toByteArray()), Marker.class); + + assertEquals(expectedMarker.getSchema().getCustomMetadata(), actualMarker.getSchema().getCustomMetadata()); + assertEquals(expectedMarker.getSchema().getFields(), actualMarker.getSchema().getFields()); + assertEquals(expectedMarker.getBound(), actualMarker.getBound()); + assertEquals(expectedMarker.getValue(), actualMarker.getValue()); + assertEquals(expectedValue, actualMarker.getValue()); + assertEquals(false, actualMarker.isNullValue()); + + logger.info("serializationTest - exit"); + } + + @Test + public void nullableSerializationTest() + throws IOException + { + logger.info("nullableSerializationTest - enter"); + + ObjectMapper serializer = ObjectMapperFactory.create(new BlockAllocatorImpl()); + Marker expectedMarker = Marker.nullMarker(allocator, Types.MinorType.INT.getType()); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.writeValue(out, expectedMarker); + + ObjectMapper deserializer = ObjectMapperFactory.create(allocator); + + Marker actualMarker = deserializer.readValue(new ByteArrayInputStream(out.toByteArray()), Marker.class); + + assertEquals(expectedMarker.getSchema().getCustomMetadata(), actualMarker.getSchema().getCustomMetadata()); + assertEquals(expectedMarker.getSchema().getFields(), actualMarker.getSchema().getFields()); + assertEquals(expectedMarker.getBound(), actualMarker.getBound()); + assertEquals(true, actualMarker.isNullValue()); + + logger.info("nullableSerializationTest - exit"); + } +} diff --git a/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/serde/ObjectMapperUtil.java b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/serde/ObjectMapperUtil.java new file mode 100644 index 0000000000..d55abc1395 --- /dev/null +++ b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/serde/ObjectMapperUtil.java @@ -0,0 +1,50 @@ +package com.amazonaws.athena.connector.lambda.serde; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import static org.junit.Assert.assertEquals; + +public class ObjectMapperUtil +{ + private ObjectMapperUtil() {} + + public static void assertSerialization(Object object, Class clazz) + { + Object actual = null; + try (BlockAllocatorImpl allocator = new BlockAllocatorImpl()) { + ObjectMapper mapper = ObjectMapperFactory.create(allocator); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + mapper.writeValue(out, object); + actual = mapper.readValue(new ByteArrayInputStream(out.toByteArray()), clazz); + assertEquals(object, actual); + } + catch (IOException | AssertionError ex) { + throw new RuntimeException(ex); + } + } +} diff --git a/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/serde/SchemaSerializationTest.java b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/serde/SchemaSerializationTest.java new file mode 100644 index 0000000000..d13532ac10 --- /dev/null +++ b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/serde/SchemaSerializationTest.java @@ -0,0 +1,78 @@ +package com.amazonaws.athena.connector.lambda.serde; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.data.SchemaSerDe; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.arrow.vector.types.FloatingPointPrecision; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.arrow.vector.types.pojo.Schema; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import static org.junit.Assert.*; + +public class SchemaSerializationTest +{ + private static final Logger logger = LoggerFactory.getLogger(SchemaSerializationTest.class); + + private final ObjectMapper objectMapper = ObjectMapperFactory.create(new BlockAllocatorImpl()); + + @Test + public void serializationTest() + throws IOException + { + logger.info("serializationTest - enter"); + SchemaBuilder schemaBuilder = new SchemaBuilder(); + schemaBuilder.addMetadata("meta1", "meta-value-1"); + schemaBuilder.addMetadata("meta2", "meta-value-2"); + schemaBuilder.addField("intfield1", new ArrowType.Int(32, true)); + schemaBuilder.addField("doublefield2", new ArrowType.FloatingPoint(FloatingPointPrecision.DOUBLE)); + schemaBuilder.addField("varcharfield3", new ArrowType.Utf8()); + Schema expectedSchema = schemaBuilder.build(); + + SchemaSerDe serDe = new SchemaSerDe(); + ByteArrayOutputStream schemaOut = new ByteArrayOutputStream(); + serDe.serialize(expectedSchema, schemaOut); + + TestPojo expected = new TestPojo(expectedSchema); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + objectMapper.writeValue(out, expected); + TestPojo actual = objectMapper.readValue(new ByteArrayInputStream(out.toByteArray()), TestPojo.class); + + Schema actualSchema = actual.getSchema(); + logger.info("serializationTest - fields[{}]", actualSchema.getFields()); + logger.info("serializationTest - meta[{}]", actualSchema.getCustomMetadata()); + + assertEquals(expectedSchema.getFields(), actualSchema.getFields()); + assertEquals(expectedSchema.getCustomMetadata(), actualSchema.getCustomMetadata()); + + logger.info("serializationTest - exit"); + } +} diff --git a/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/serde/TestPojo.java b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/serde/TestPojo.java new file mode 100644 index 0000000000..5aaf0e9db2 --- /dev/null +++ b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/serde/TestPojo.java @@ -0,0 +1,41 @@ +package com.amazonaws.athena.connector.lambda.serde; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.apache.arrow.vector.types.pojo.Schema; + +public class TestPojo +{ + private final Schema schema; + + @JsonCreator + public TestPojo(@JsonProperty("schema") Schema schema) + { + this.schema = schema; + } + + public Schema getSchema() + { + return schema; + } +} diff --git a/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/udf/UserDefinedFunctionHandlerTest.java b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/udf/UserDefinedFunctionHandlerTest.java new file mode 100644 index 0000000000..fd44c8ae15 --- /dev/null +++ b/athena-federation-sdk/src/test/java/com/amazonaws/athena/connector/lambda/udf/UserDefinedFunctionHandlerTest.java @@ -0,0 +1,357 @@ +package com.amazonaws.athena.connector.lambda.udf; + +/*- + * #%L + * Amazon Athena Query Federation SDK + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.amazonaws.athena.connector.lambda.data.BlockUtils; +import com.amazonaws.athena.connector.lambda.data.FieldBuilder; +import com.amazonaws.athena.connector.lambda.data.FieldResolver; +import com.amazonaws.athena.connector.lambda.data.UnitTestBlockUtils; +import com.amazonaws.athena.connector.lambda.request.FederationRequest; +import com.amazonaws.athena.connector.lambda.request.PingRequest; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import org.apache.arrow.vector.FieldVector; +import org.apache.arrow.vector.Float4Vector; +import org.apache.arrow.vector.Float8Vector; +import org.apache.arrow.vector.IntVector; +import org.apache.arrow.vector.VarCharVector; +import org.apache.arrow.vector.complex.ListVector; +import org.apache.arrow.vector.complex.StructVector; +import org.apache.arrow.vector.complex.reader.FieldReader; +import org.apache.arrow.vector.types.FloatingPointPrecision; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.arrow.vector.types.pojo.FieldType; +import org.apache.arrow.vector.types.pojo.Schema; +import org.apache.arrow.vector.util.Text; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static com.amazonaws.athena.connector.lambda.udf.UserDefinedFunctionType.SCALAR; +import static junit.framework.TestCase.assertTrue; +import static org.junit.Assert.*; + +public class UserDefinedFunctionHandlerTest +{ + private static final String COLUMN_PREFIX = "col_"; + + private TestUserDefinedFunctionHandler handler; + + private BlockAllocatorImpl allocator; + + @Before + public void setUp() + { + handler = new TestUserDefinedFunctionHandler(); + allocator = new BlockAllocatorImpl(); + } + + @After + public void tearDown() + { + allocator.close(); + } + + @Test + public void testInvocationWithBasicType() + { + int rowCount = 20; + UserDefinedFunctionRequest udfRequest = createUDFRequest(rowCount, Integer.class, "testScalarUDF", true, Integer.class, Integer.class); + + UserDefinedFunctionResponse udfResponse = handler.processFunction(allocator, udfRequest); + Block responseBlock = udfResponse.getRecords(); + + assertEquals(1, responseBlock.getFieldReaders().size()); + assertEquals(rowCount, responseBlock.getRowCount()); + + FieldReader fieldReader = responseBlock.getFieldReaders().get(0); + + for (int pos = 0; pos < rowCount; ++pos) { + fieldReader.setPosition(pos); + int val = (int) UnitTestBlockUtils.getValue(fieldReader, pos); + int expected = handler.testScalarUDF(pos + 100, pos + 100); + assertEquals(expected, val); + } + } + + @Test + public void testInvocationWithListType() + { + int rowCount = 20; + UserDefinedFunctionRequest udfRequest = createUDFRequest(rowCount, List.class, "testListType", true, List.class); + + UserDefinedFunctionResponse udfResponse = handler.processFunction(allocator, udfRequest); + Block responseBlock = udfResponse.getRecords(); + + assertEquals(1, responseBlock.getFieldReaders().size()); + assertEquals(rowCount, responseBlock.getRowCount()); + + FieldReader fieldReader = responseBlock.getFieldReaders().get(0); + + for (int pos = 0; pos < rowCount; ++pos) { + fieldReader.setPosition(pos); + List result = (List) UnitTestBlockUtils.getValue(fieldReader, pos); + List expected = handler.testListType(ImmutableList.of(pos + 100, pos + 200, pos + 300)); + assertArrayEquals(expected.toArray(), result.toArray()); + } + } + + @Test + public void testInvocationWithStructType() + { + int rowCount = 20; + UserDefinedFunctionRequest udfRequest = createUDFRequest(rowCount, Map.class, "testRowType", true, Map.class); + + UserDefinedFunctionResponse udfResponse = handler.processFunction(allocator, udfRequest); + Block responseBlock = udfResponse.getRecords(); + + assertEquals(1, responseBlock.getFieldReaders().size()); + assertEquals(rowCount, responseBlock.getRowCount()); + + FieldReader fieldReader = responseBlock.getFieldReaders().get(0); + + for (int pos = 0; pos < rowCount; ++pos) { + fieldReader.setPosition(pos); + Map actual = (Map) UnitTestBlockUtils.getValue(fieldReader, pos); + + Map input = ImmutableMap.of("intVal", pos + 100, "doubleVal", pos + 200.2); + Map expected = handler.testRowType(input); + + for (Map.Entry entry : expected.entrySet()) { + String key = entry.getKey(); + assertTrue(actual.containsKey(key)); + assertEquals(expected.get(key), actual.get(key)); + } + } + } + + @Test + public void testInvocationWithNullVAlue() + { + int rowCount = 20; + UserDefinedFunctionRequest udfRequest = createUDFRequest(rowCount, Boolean.class, "testScalarUDFWithNullCheck", false, Integer.class); + + UserDefinedFunctionResponse udfResponse = handler.processFunction(allocator, udfRequest); + Block responseBlock = udfResponse.getRecords(); + + assertEquals(1, responseBlock.getFieldReaders().size()); + assertEquals(rowCount, responseBlock.getRowCount()); + + FieldReader fieldReader = responseBlock.getFieldReaders().get(0); + + for (int pos = 0; pos < rowCount; ++pos) { + fieldReader.setPosition(pos); + assertTrue(fieldReader.isSet()); + Boolean expected = handler.testScalarUDFWithNullCheck(null); + Boolean actual = fieldReader.readBoolean(); + assertEquals(expected, actual); + } + } + + @Test + public void testRequestTypeValidation() throws Exception + { + FederationRequest federationRequest = new PingRequest(null, "dummy_catalog", "dummy_qid"); + + ObjectMapper objectMapper = new ObjectMapper(); + + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + objectMapper.writeValue(byteArrayOutputStream, federationRequest); + byte[] inputData = byteArrayOutputStream.toByteArray(); + ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(inputData); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + + try { + handler.handleRequest(byteArrayInputStream, outputStream, null); + fail(); + } + catch (Exception e) { + assertTrue(e.getMessage().contains("Expected a UserDefinedFunctionRequest but found")); + } + } + + private UserDefinedFunctionRequest createUDFRequest(int rowCount, Class returnType, String methodName, boolean nonNullData, Class... argumentTypes) + { + Schema inputSchema = buildSchema(argumentTypes); + Schema outputSchema = buildSchema(returnType); + + Block block = allocator.createBlock(inputSchema); + block.setRowCount(rowCount); + if (nonNullData) { + writeData(block, rowCount); + } + + return new UserDefinedFunctionRequest(null, block, outputSchema, methodName, SCALAR); + } + + private void writeData(Block block, int numOfRows) + { + for (FieldVector fieldVector : block.getFieldVectors()) { + fieldVector.setInitialCapacity(numOfRows); + fieldVector.allocateNew(); + fieldVector.setValueCount(numOfRows); + + for (int idx = 0; idx < numOfRows; ++idx) { + writeColumn(fieldVector, idx); + } + } + } + + private void writeColumn(FieldVector fieldVector, int idx) + { + if (fieldVector instanceof IntVector) { + IntVector intVector = (IntVector) fieldVector; + intVector.setSafe(idx, idx + 100); + return; + } + + if (fieldVector instanceof Float4Vector) { + Float4Vector float4Vector = (Float4Vector) fieldVector; + float4Vector.setSafe(idx, idx + 100.1f); + return; + } + + if (fieldVector instanceof Float8Vector) { + Float8Vector float8Vector = (Float8Vector) fieldVector; + float8Vector.setSafe(idx, idx + 100.2); + return; + } + + if (fieldVector instanceof VarCharVector) { + VarCharVector varCharVector = (VarCharVector) fieldVector; + varCharVector.setSafe(idx, new Text(idx + "-my-varchar")); + return; + } + + if (fieldVector instanceof ListVector) { + BlockUtils.setComplexValue(fieldVector, + idx, + FieldResolver.DEFAULT, + ImmutableList.of(idx + 100, idx + 200, idx + 300)); + return; + } + + if (fieldVector instanceof StructVector) { + Map input = ImmutableMap.of("intVal", idx + 100, "doubleVal", idx + 200.2); + BlockUtils.setComplexValue(fieldVector, + idx, + FieldResolver.DEFAULT, + input); + return; + } + + throw new IllegalArgumentException("Unsupported fieldVector " + fieldVector.getClass().getCanonicalName()); + } + + private Schema buildSchema(Class... types) + { + ImmutableList.Builder fieldsBuilder = ImmutableList.builder(); + for (int i = 0; i < types.length; ++i) { + String columnName = COLUMN_PREFIX + i; + Field field = getArrowField(types[i], columnName); + fieldsBuilder.add(field); + } + return new Schema(fieldsBuilder.build(), null); + } + + private Field getArrowField(Class type, String columnName) + { + if (type == Integer.class) { + return new Field(columnName, FieldType.nullable(new ArrowType.Int(32, true)), null); + } + + if (type == Float.class) { + return new Field(columnName, FieldType.nullable(new ArrowType.FloatingPoint(FloatingPointPrecision.SINGLE)), null); + } + + if (type == Double.class) { + return new Field(columnName, FieldType.nullable(new ArrowType.FloatingPoint(FloatingPointPrecision.DOUBLE)), null); + } + + if (type == String.class) { + return new Field(columnName, FieldType.nullable(new ArrowType.Utf8()), null); + } + + if (type == Boolean.class) { + return new Field(columnName, FieldType.nullable(new ArrowType.Bool()), null); + } + + if (type == List.class) { + Field childField = new Field(columnName, FieldType.nullable(new ArrowType.Int(32, true)), null); + return new Field(columnName, FieldType.nullable(Types.MinorType.LIST.getType()), + Collections.singletonList(childField)); + } + + if (type == Map.class) { + FieldBuilder fieldBuilder = FieldBuilder.newBuilder(columnName, Types.MinorType.STRUCT.getType()); + + Field childField1 = new Field("intVal", FieldType.nullable(new ArrowType.Int(32, true)), null); + Field childField2 = new Field("doubleVal", FieldType.nullable(new ArrowType.FloatingPoint(FloatingPointPrecision.DOUBLE)), null);; + + fieldBuilder.addField(childField1); + fieldBuilder.addField(childField2); + + return fieldBuilder.build(); + } + + throw new IllegalArgumentException("Unsupported type " + type); + } + + private static class TestUserDefinedFunctionHandler extends UserDefinedFunctionHandler + { + public Integer testScalarUDF(Integer col1, Integer col2) + { + return col1 + col2; + } + + public Boolean testScalarUDFWithNullCheck(Integer col1) { + if (col1 == null) { + return true; + } + return false; + } + + public List testListType(List input) + { + return input.stream().map(val -> val + 1).collect(Collectors.toList()); + } + + public Map testRowType(Map input) + { + Integer intVal = (Integer) input.get("intVal"); + Double doubleVal = (Double) input.get("doubleVal"); + + return ImmutableMap.of("intVal", intVal + 1, "doubleVal", doubleVal + 1.0); + } + } +} diff --git a/athena-federation-sdk/src/test/resources/log4j.properties b/athena-federation-sdk/src/test/resources/log4j.properties new file mode 100644 index 0000000000..15b502c445 --- /dev/null +++ b/athena-federation-sdk/src/test/resources/log4j.properties @@ -0,0 +1,26 @@ +### +# #%L +# Amazon Athena Query Federation SDK +# %% +# Copyright (C) 2019 Amazon Web Services +# %% +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# #L% +### +log = . +log4j.rootLogger = INFO, LAMBDA + +#Define the LAMBDA appender +log4j.appender.LAMBDA=com.amazonaws.services.lambda.runtime.log4j.LambdaAppender +log4j.appender.LAMBDA.layout=org.apache.log4j.PatternLayout +log4j.appender.LAMBDA.layout.conversionPattern=%d{yyyy-MM-dd HH:mm:ss} <%X{AWSRequestId}> %-5p %c{1}:%m%n diff --git a/athena-hbase/LICENSE.txt b/athena-hbase/LICENSE.txt new file mode 100644 index 0000000000..418de4c108 --- /dev/null +++ b/athena-hbase/LICENSE.txt @@ -0,0 +1,174 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. \ No newline at end of file diff --git a/athena-hbase/README.md b/athena-hbase/README.md new file mode 100644 index 0000000000..060cb4fa7f --- /dev/null +++ b/athena-hbase/README.md @@ -0,0 +1,87 @@ +# Amazon Athena HBase Connector + +This connector enables Amazon Athena to communicate with your HBase instance(s), making your HBase data accessible via SQL. + +Unlike traditional relational data stores, HBase tables do not have set schema. Each entry can have different fields and data types. While we are investigating the best way to support schema-on-read usecases for this connector, it presently supports two mechanisms for generating traditional table schema information. The default mechanism is for the connector to scan a small number of documents in your collection in order to form a union of all fields and coerce fields with non-overlapping data types. This basic schema inference works well for collections that have mostly uniform entries. For more diverse collections, the connector supports retrieving meta-data from the Glue Data Catalog. If the connector sees a Glue database and table which match your HBase namespace and collection names it will use the corresponding Glue table for schema. We recommend creating your Glue table such that it is a superset of all fields you may want to access from your HBase table. + +## Usage + +### Parameters + +The Athena HBase Connector supports several configuration options via Lambda environment variables. More detail on the available parameters can be found below. + +1. **spill_bucket** - When the data returned by your Lambda function exceeds Lambda’s limits, this is the bucket that the data will be written to for Athena to read the excess from. (e.g. my_bucket) +2. **spill_prefix** - (Optional) Defaults to sub-folder in your bucket called 'athena-federation-spill'. Used in conjunction with spill_bucket, this is the path within the above bucket that large responses are spilled to. You should configure an S3 lifecycle on this location to delete old spills after X days/Hours. +3. **kms_key_id** - (Optional) By default any data that is spilled to S3 is encrypted using AES-GCM and a randomly generated key. Setting a KMS Key ID allows your Lambda function to use KMS for key generation for a stronger source of encryption keys. (e.g. a7e63k4b-8loc-40db-a2a1-4d0en2cd8331) +4. **disable_spill_encryption** - (Optional) Defaults to False so that any data that is spilled to S3 is encrypted using AES-GMC either with a randomly generated key or using KMS to generate keys. Setting this to false will disable spill encryption. You may wish to disable this for improved performance, especially if your spill location in S3 uses S3 Server Side Encryption. (e.g. True or False) +5. **disable_glue** - (Optional) If present, with any valye, the connector will no longer attempt to retrieve supplemental metadata from Glue. +6. **glue_catalog** - (Optional) Can be used to target a cross-account Glue catalog. By default the connector will attempt to get metadata from its own Glue account. +7. **default_hbase** If present, this HBase connection string (e.g. master_hostname:zookeeper_port:hbase_port) is used when there is not a catalog specific environment variable (as explained below). + +You can also provide one or more properties which define the HBase connection details for the HBase instance(s) you'd like this connector to use. You can do this by setting a Lambda environment variable that corresponds to the catalog name you'd like to use in Athena. For example, if I'd like to query two different HBase instances from Athena in the below queries: + +```sql + select * from "hbase_instance_1".database.table + select * from "hbase_instance_2".database.table + ``` + +To support these two SQL statements we'd need to add two environment variables to our Lambda function: + +1. **hbase_instance_1** - The value should be the HBase connection details in the format of: master_hostname:zookeeper_port:hbase_port +2. **hbase_instance_2** - The value should be the HBase connection details in the format of: master_hostname:zookeeper_port:hbase_port + +You can also optionally use SecretsManager for part or all of the value for the preceeding connection details. For example, if I set a Lambda environment variable for **hbase_instance_1** to be "${hbase_host_1}:${hbase_zookeeper_port_1}:${hbase_master_port_1}" the Athena Federation SDK will automatically attempt to retrieve a secret from AWS SecretsManager named "hbase_host_1" and inject that value in place of "${hbase_host_1}". It wil do the same for the other secrets: hbase_zookeeper_port_1, hbase_master_port_1. Basically anything between ${...} is attempted as a secret in SecretsManager. If no such secret exists, the text isn't replaced. + + +### Setting Up Databases & Tables + +To enable a Glue Table for use with HBase, you simply need to have a Glue database and table that matches any HBase Namespace and Table that you'd like to supply supplemental metadata for (instead of relying on the HBase Connector's ability to infer schema). The connector's in built schema inference only supports values serialized in HBase as Strings (e.g. String.valueOf(int)). You can enable a Glue table to be used for supplemental metadata by seting the below table properties from the Glue Console when editing the Table in question. The only other thing you need to do ensure you use the appropriate data types and, optionally, HBase column family naming conventions. + +1. **hbase-metadata-flag** - Flag indicating that the table can be used for supplemental meta-data by the Athena HBase Connector. The value is unimportant as long as this key is present in the properties of the table. +1. **hbase-native-storage-flag** - This flag toggles the two modes of value serialization supported by the connector. By default (when this field is not present) the connector assumes all values are stored in HBase as strings. As such it will attempt to parse INT, BIGINT, DOUBLE, etc.. from HBase as Strings. If this field is set (the value of the table property doesn't matter, only its presence) on the table in Glue, the connector will switch to 'native' storage mode and attempt to read INT, BIGINT, BIT, and DOUBLE as bytes by using ByteBuffer.wrap(value).getInt(), ByteBuffer.wrap(value).getLong(), ByteBuffer.wrap(value).get(), and ByteBuffer.wrap(value).getDouble(). + +When it comes to setting your columns, you have two choices for how you model HBase column families. The Athena HBase connector supports fully qualified (aka flattened) naming like "family:column" as well as using STRUCTS to model your column families. In the STRUCT model the name of the STRUCT field should match the column family and then any children of that STRUCT should match the names of the columns in that family. Since predicate push down and columnar reads are not yet fully supported for complex types like STRUCTs we recommend against using the STRUCT approach unless your usecase specifically requires the use of STRUCTS. The below image shows how we've configured a table in Glue using a combination of these approaches. + + ![Glue Example Image](https://github.com/awslabs/aws-athena-query-federation/blob/master/docs/img/hbase_glue_example.png?raw=true) + +### Data Types + +All HBase values are retrieved as the basic byte type. From there they are converted to one of the below Apache Arrow data types used by the Athena Query Federation SDK based on how you've defined your table(s) in Glue's DataCatalog. If you are not using Glue to supplement your metedata and instead depending on the connector's schema inference capabilities, only a subset of the below data types will be used, namely: BIGINT, FLOAT8, VARCHAR. + +|Glue DataType|Apache Arrow Type| +|-------------|-----------------| +|int|INT| +|bigint|BIGINT| +|double|FLOAT8| +|float|FLOAT4| +|boolean|BIT| +|binary|VARBINARY| +|string|VARCHAR| + + +### Required Permissions + +Review the "Policies" section of the athena-hbase.yaml file for full details on the IAM Policies required by this connector. A brief summary is below. + +1. S3 Write Access - In order to successfully handle large queries, the connector requires write access to a location in S3. +2. SecretsManager Read Access - If you choose to store HBase endpoint details in SecretsManager you will need to grant the connector access to those secrets. +3. Glue Data Catalog - Since HBase does not have a meta-data store, the connector requires Read-Only access to Glue's DataCatalog for obtaining HBase key to table/column mappings. +4. VPC Access - In order to connect to your VPC for the purposes of communicating with your HBase instance(s), the connector needs the ability to attach/detach an interface to the VPC. +5. CloudWatch Logs - This is a somewhat implicit permission when deploying a Lambda function but it needs access to cloudwatch logs for storing logs. +1. Athena GetQueryExecution - The connector uses this access to fast-fail when the upstream Athena query has terminated. + +### Deploying The Connector + +To use the Amazon Athena HBase Connector in your queries, navigate to AWS Serverless Application Repository and deploy a pre-built version of this connector. Alternatively, you can build and deploy this connector from source follow the below steps or use the more detailed tutorial in the athena-example module: + +1. From the athena-federation-sdk dir, run `mvn clean install` if you haven't already. +2. From the athena-hbase dir, run `mvn clean install`. +3. From the athena-hbase dir, run `../tools/publish.sh S3_BUCKET_NAME athena-hbase` to publish the connector to your private AWS Serverless Application Repository. The S3_BUCKET in the command is where a copy of the connector's code will be stored for Serverless Application Repository to retrieve it. This will allow users with permission to do so, the ability to deploy instances of the connector via 1-Click form. Then navigate to [Serverless Application Repository](https://aws.amazon.com/serverless/serverlessrepo) + +## Performance + +The Athena HBase Connector will attempt to parallelize queries against your HBase instance by reading each region server in parallel. Predicate Pushdown is performed within the Lambda function and, where possible, push down into HBase using filters. + +## License + +This project is licensed under the Apache-2.0 License. \ No newline at end of file diff --git a/athena-hbase/athena-hbase.yaml b/athena-hbase/athena-hbase.yaml new file mode 100644 index 0000000000..6cce9c041a --- /dev/null +++ b/athena-hbase/athena-hbase.yaml @@ -0,0 +1,97 @@ +Transform: 'AWS::Serverless-2016-10-31' +Metadata: + 'AWS::ServerlessRepo::Application': + Name: AthenaHBaseConnector + Description: 'This connector enables Amazon Athena to communicate with your HBase instance(s), making your HBase data accessible via SQL.' + Author: 'Amazon Athena' + SpdxLicenseId: Apache-2.0 + LicenseUrl: LICENSE.txt + ReadmeUrl: README.md + Labels: + - athena-federation + HomePageUrl: 'https://github.com/awslabs/aws-athena-query-federation' + SemanticVersion: 1.0.0 + SourceCodeUrl: 'https://github.com/awslabs/aws-athena-query-federation' +Parameters: + AthenaCatalogName: + Description: 'The name you will give to this catalog in Athena. It will also be used as the function name.' + Type: String + SpillBucket: + Description: 'The bucket where this function can spill data.' + Type: String + Default: athena-federation-spill + SpillPrefix: + Description: 'The bucket prefix where this function can spill large responses.' + Type: String + Default: athena-spill + LambdaTimeout: + Description: 'Maximum Lambda invocation runtime in seconds. (min 1 - 900 max)' + Default: 900 + Type: Number + LambdaMemory: + Description: 'Lambda memory in MB (min 128 - 3008 max).' + Default: 3008 + Type: Number + DisableSpillEncryption: + Description: "WARNING: If set to 'true' encryption for spilled data is disabled." + Default: 'false' + Type: String + SecurityGroupIds: + Description: 'One or more SecurityGroup IDs corresponding to the SecurityGroup that should be applied to the Lambda function. (e.g. sg1,sg2,sg3)' + Type: 'List' + SubnetIds: + Description: 'One or more Subnet IDs corresponding to the Subnet that the Lambda function can use to access you data source. (e.g. subnet1,subnet2)' + Type: 'List' + SecretNameOrPrefix: + Description: 'The name or prefix of a set of names within Secrets Manager that this function should have access to. (e.g. hbase-*).' + Type: String + HBaseConnectionString: + Description: 'The HBase connection details to use by default in the format: master_hostname:zookeeper_port:hbase_port and optionally using SecretsManager (e.g. ${secret_name}).' + Type: String +Resources: + ConnectorConfig: + Type: 'AWS::Serverless::Function' + Properties: + Environment: + Variables: + disable_spill_encryption: !Ref DisableSpillEncryption + spill_bucket: !Ref SpillBucket + spill_prefix: !Ref SpillPrefix + default_hbase: !Ref HBaseConnectionString + FunctionName: !Ref AthenaCatalogName + Handler: "com.amazonaws.athena.connectors.hbase.HbaseCompositeHandler" + CodeUri: "./target/athena-hbase-1.0.jar" + Description: "Enables Amazon Athena to communicate with HBase, making your HBase data accessible via SQL" + Runtime: java8 + Timeout: !Ref LambdaTimeout + MemorySize: !Ref LambdaMemory + Policies: + - Statement: + - Action: + - secretsmanager:GetSecretValue + Effect: Allow + Resource: !Sub 'arn:aws:secretsmanager:*:*:secret:${SecretNameOrPrefix}' + Version: '2012-10-17' + - Statement: + - Action: + - glue:GetTableVersions + - glue:GetPartitions + - glue:GetTables + - glue:GetTableVersion + - glue:GetDatabases + - glue:GetTable + - glue:GetPartition + - glue:GetDatabase + - athena:GetQueryExecution + Effect: Allow + Resource: '*' + Version: '2012-10-17' + #S3CrudPolicy allows our connector to spill large responses to S3. You can optionally replace this pre-made policy + #with one that is more restrictive and can only 'put' but not read,delete, or overwrite files. + - S3CrudPolicy: + BucketName: !Ref SpillBucket + #VPCAccessPolicy allows our connector to run in a VPC so that it can access your data source. + - VPCAccessPolicy: {} + VpcConfig: + SecurityGroupIds: !Ref SecurityGroupIds + SubnetIds: !Ref SubnetIds \ No newline at end of file diff --git a/athena-hbase/pom.xml b/athena-hbase/pom.xml new file mode 100644 index 0000000000..55624efb26 --- /dev/null +++ b/athena-hbase/pom.xml @@ -0,0 +1,76 @@ + + + + aws-athena-query-federation + com.amazonaws + 1.0 + + 4.0.0 + + athena-hbase + + + + com.amazonaws + aws-athena-federation-sdk + ${aws-athena-federation-sdk.version} + + + com.amazonaws + aws-java-sdk-glue + 1.11.490 + + + org.apache.hbase + hbase-client + 1.4.10 + + + + org.apache.httpcomponents + httpclient + 4.5.6 + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.1 + + false + + + + package + + shade + + + + + *:* + + org/apache/hadoop/yarn/webapp/** + org/apache/hadoop/mapred/** + org/apache/hadoop/mapreduce/** + org/apache/hadoop/yarn/** + org/apache/curator/** + org/apache/directory/** + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + + + + \ No newline at end of file diff --git a/athena-hbase/src/main/java/com/amazonaws/athena/connectors/hbase/HbaseCompositeHandler.java b/athena-hbase/src/main/java/com/amazonaws/athena/connectors/hbase/HbaseCompositeHandler.java new file mode 100644 index 0000000000..9dc9eb7f4c --- /dev/null +++ b/athena-hbase/src/main/java/com/amazonaws/athena/connectors/hbase/HbaseCompositeHandler.java @@ -0,0 +1,35 @@ +/*- + * #%L + * athena-hbase + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.hbase; + +import com.amazonaws.athena.connector.lambda.handlers.CompositeHandler; + +/** + * Boilerplate composite handler that allows us to use a single Lambda function for both + * Metadata and Data. In this case we just compose HbaseMetadataHandler and HbaseRecordHandler. + */ +public class HbaseCompositeHandler + extends CompositeHandler +{ + public HbaseCompositeHandler() + { + super(new HbaseMetadataHandler(), new HbaseRecordHandler()); + } +} diff --git a/athena-hbase/src/main/java/com/amazonaws/athena/connectors/hbase/HbaseConnectionFactory.java b/athena-hbase/src/main/java/com/amazonaws/athena/connectors/hbase/HbaseConnectionFactory.java new file mode 100644 index 0000000000..ebe2eaa1eb --- /dev/null +++ b/athena-hbase/src/main/java/com/amazonaws/athena/connectors/hbase/HbaseConnectionFactory.java @@ -0,0 +1,156 @@ +/*- + * #%L + * athena-hbase + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.hbase; + +import org.apache.arrow.util.VisibleForTesting; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.HBaseConfiguration; +import org.apache.hadoop.hbase.client.Connection; +import org.apache.hadoop.hbase.client.ConnectionFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * Creates and Caches HBase Connection Instances, using the connection string as the cache key. + * + * @Note Connection String format is expected to be host:zookeeper_port:master_port + */ +public class HbaseConnectionFactory +{ + private static final Logger logger = LoggerFactory.getLogger(HbaseConnectionFactory.class); + + private final Map clientCache = new HashMap<>(); + + private final Map defaultClientConfig = new HashMap<>(); + + public HbaseConnectionFactory() + { + setClientConfig("hbase.rpc.timeout", "2000"); + setClientConfig("hbase.client.retries.number", "3"); + setClientConfig("hbase.client.pause", "500"); + setClientConfig("zookeeper.recovery.retry", "2"); + } + + /** + * Used to set HBase client config options that should be applied to all future connections. + * + * @param name The name of the property (e.g. hbase.rpc.timeout). + * @param value The value of the property to set on the HBase client config object before construction. + */ + public synchronized void setClientConfig(String name, String value) + { + defaultClientConfig.put(name, value); + } + + /** + * Provides access to the current HBase client config options used during connection construction. + * + * @return Map where the Key is the config name and the value is the config value. + * @note This can be helpful when logging diagnostic info. + */ + public synchronized Map getClientConfigs() + { + return Collections.unmodifiableMap(defaultClientConfig); + } + + /** + * Gets or Creates an HBase connection for the given connection string. + * + * @param conStr HBase connection details, format is expected to be host:zookeeper_port:master_port + * @return An HBase connection if the connection succeeded, else the function will throw. + */ + public synchronized Connection getOrCreateConn(String conStr) + { + logger.info("getOrCreateConn: enter"); + Connection conn = clientCache.get(conStr); + + if (conn == null || !connectionTest(conn)) { + String[] endpointParts = conStr.split(":"); + if (endpointParts.length == 3) { + conn = createConnection(endpointParts[0], endpointParts[1], endpointParts[2]); + clientCache.put(conStr, conn); + } + else { + throw new IllegalArgumentException("Hbase endpoint format error."); + } + } + + logger.info("getOrCreateConn: exit"); + return conn; + } + + private Connection createConnection(String host, String masterPort, String zookeeperPort) + { + try { + logger.info("createConnection: enter"); + Configuration config = HBaseConfiguration.create(); + config.set("hbase.zookeeper.quorum", host); + config.set("hbase.zookeeper.property.clientPort", zookeeperPort); + config.set("hbase.master", host + ":" + masterPort); + for (Map.Entry nextConfig : defaultClientConfig.entrySet()) { + logger.info("createConnection: applying client config {}:{}", nextConfig.getKey(), nextConfig.getValue()); + config.set(nextConfig.getKey(), nextConfig.getValue()); + } + Connection conn = ConnectionFactory.createConnection(config); + logger.info("createConnection: hbase.zookeeper.quorum:" + config.get("hbase.zookeeper.quorum")); + logger.info("createConnection: hbase.zookeeper.property.clientPort:" + config.get("hbase.zookeeper.property.clientPort")); + logger.info("createConnection: hbase.master:" + config.get("hbase.master")); + return conn; + } + catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + /** + * Runs a 'quick' test on the connection and then returns it if it passes. + */ + private boolean connectionTest(Connection conn) + { + try { + logger.info("connectionTest: Testing connection started."); + conn.getAdmin().listTableNames(); + logger.info("connectionTest: Testing connection completed - success."); + return true; + } + catch (RuntimeException | IOException ex) { + logger.warn("getOrCreateConn: Exception while testing existing connection.", ex); + } + logger.info("connectionTest: Testing connection completed - fail."); + return false; + } + + /** + * Injects a connection into the client cache. + * + * @param conStr The connection string (aka the cache key) + * @param conn The connection to inject into the client cache, most often a Mock used in testing. + */ + @VisibleForTesting + protected synchronized void addConnection(String conStr, Connection conn) + { + clientCache.put(conStr, conn); + } +} diff --git a/athena-hbase/src/main/java/com/amazonaws/athena/connectors/hbase/HbaseFieldResolver.java b/athena-hbase/src/main/java/com/amazonaws/athena/connectors/hbase/HbaseFieldResolver.java new file mode 100644 index 0000000000..627f5b2aa6 --- /dev/null +++ b/athena-hbase/src/main/java/com/amazonaws/athena/connectors/hbase/HbaseFieldResolver.java @@ -0,0 +1,74 @@ +/*- + * #%L + * athena-hbase + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.hbase; + +import com.amazonaws.athena.connector.lambda.data.FieldResolver; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.hadoop.hbase.client.Result; + +/** + * Used to resolve and convert complex types from HBase to Apache Arrow's type system + * when using BlockUtils.setComplexValue(...). + */ +public class HbaseFieldResolver + implements FieldResolver +{ + private final byte[] family; + private final boolean isNative; + + /** + * @param isNative True if the values are stored as native byte arrays in HBase. + * @param family The HBase column family that this field resolver is for. + */ + public HbaseFieldResolver(boolean isNative, byte[] family) + { + this.isNative = isNative; + this.family = family; + } + + /** + * Static construction helper. + * + * @param isNative True if the values are stored as native byte arrays in HBase. + * @param family The HBase column family that this field resolver is for. + */ + public static HbaseFieldResolver resolver(boolean isNative, String family) + { + return new HbaseFieldResolver(isNative, family.getBytes()); + } + + /** + * @param field The Apache Arrow field we'd like to extract from the val. + * @param val The value from which we'd like to extract the provide field. + * @return Object containing the value for the requested field. + * @see FieldResolver in the Athena Query Federation SDK + */ + @Override + public Object getFieldValue(Field field, Object val) + { + if (!(val instanceof Result)) { + String clazz = (val != null) ? val.getClass().getName() : "null"; + throw new IllegalArgumentException("Expected value of type Result but found " + clazz); + } + + byte[] rawFieldValue = ((Result) val).getValue(family, field.getName().getBytes()); + return HbaseSchemaUtils.coerceType(isNative, field.getType(), rawFieldValue); + } +} diff --git a/athena-hbase/src/main/java/com/amazonaws/athena/connectors/hbase/HbaseMetadataHandler.java b/athena-hbase/src/main/java/com/amazonaws/athena/connectors/hbase/HbaseMetadataHandler.java new file mode 100644 index 0000000000..54219c6c43 --- /dev/null +++ b/athena-hbase/src/main/java/com/amazonaws/athena/connectors/hbase/HbaseMetadataHandler.java @@ -0,0 +1,291 @@ +/*- + * #%L + * athena-hbase + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.hbase; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockWriter; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.handlers.GlueMetadataHandler; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesResponse; +import com.amazonaws.athena.connector.lambda.metadata.MetadataRequest; +import com.amazonaws.athena.connector.lambda.metadata.glue.GlueFieldLexer; +import com.amazonaws.athena.connector.lambda.security.EncryptionKeyFactory; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.glue.AWSGlue; +import com.amazonaws.services.glue.AWSGlueClientBuilder; +import com.amazonaws.services.glue.model.Table; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import org.apache.arrow.util.VisibleForTesting; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.arrow.vector.types.pojo.Schema; +import org.apache.hadoop.hbase.HRegionInfo; +import org.apache.hadoop.hbase.NamespaceDescriptor; +import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.client.Admin; +import org.apache.hadoop.hbase.client.Connection; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Handles metadata requests for the Athena HBase Connector. + *

+ * For more detail, please see the module's README.md, some notable characteristics of this class include: + *

+ * 1. Uses a Glue table property (hbase-metadata-flag) to indicate that the table (whose name matched the HBase table + * name) can indeed be used to supplement metadata from HBase itself. + * 2. Uses a Glue table property (hbase-native-storage-flag) to indicate that the table is stored in HBase + * using native byte storage (e.g. int as 4 BYTES instead of int serialized as a String). + * 3. Attempts to resolve sensitive fields such as HBase connection strings via SecretsManager so that you can substitute + * variables with values from by doing something like hostname:port:password=${my_secret} + */ +public class HbaseMetadataHandler + extends GlueMetadataHandler +{ + //FLAG used to indicate the given table is stored using HBase native formatting not as strings + protected static final String HBASE_NATIVE_STORAGE_FLAG = "hbase-native-storage-flag"; + //Field name used to store the connection string as a property on Split objects. + protected static final String HBASE_CONN_STR = "connStr"; + //Field name used to store the HBase scan start key as a property on Split objects. + protected static final String START_KEY_FIELD = "start_key"; + //Field name used to store the HBase scan end key as a property on Split objects. + protected static final String END_KEY_FIELD = "end_key"; + //Field name used to store the HBase region id as a property on Split objects. + protected static final String REGION_ID_FIELD = "region_id"; + //Field name used to store the HBase region name as a property on Split objects. + protected static final String REGION_NAME_FIELD = "region_name"; + private static final Logger logger = LoggerFactory.getLogger(HbaseMetadataHandler.class); + //The Env variable name used to store the default HBase connection string if no catalog specific + //env variable is set. + private static final String DEFAULT_HBASE = "default_hbase"; + //The Glue table property that indicates that a table matching the name of an HBase table + //is indeed enabled for use by this connector. + private static final String HBASE_METADATA_FLAG = "hbase-metadata-flag"; + //Used to filter out Glue tables which lack HBase metadata flag. + private static final TableFilter TABLE_FILTER = (Table table) -> table.getParameters().containsKey(HBASE_METADATA_FLAG); + //The Env variable name used to indicate that we want to disable the use of Glue DataCatalog for supplemental + //metadata and instead rely solely on the connector's schema inference capabilities. + private static final String GLUE_ENV_VAR = "disable_glue"; + //Used to denote the 'type' of this connector for diagnostic purposes. + private static final String SOURCE_TYPE = "hbase"; + //The number of rows to scan when attempting to infer schema from an HBase table. + private static final int NUM_ROWS_TO_SCAN = 10; + private final AWSGlue awsGlue; + private final HbaseConnectionFactory connectionFactory; + + public HbaseMetadataHandler() + { + super((System.getenv(GLUE_ENV_VAR) == null) ? AWSGlueClientBuilder.standard().build() : null, SOURCE_TYPE); + this.awsGlue = getAwsGlue(); + this.connectionFactory = new HbaseConnectionFactory(); + } + + @VisibleForTesting + protected HbaseMetadataHandler(AWSGlue awsGlue, + EncryptionKeyFactory keyFactory, + AWSSecretsManager secretsManager, + AmazonAthena athena, + HbaseConnectionFactory connectionFactory, + String spillBucket, + String spillPrefix) + { + super(awsGlue, keyFactory, secretsManager, athena, SOURCE_TYPE, spillBucket, spillPrefix); + this.awsGlue = awsGlue; + this.connectionFactory = connectionFactory; + } + + private Connection getOrCreateConn(MetadataRequest request) + { + String endpoint = resolveSecrets(getConnStr(request)); + return connectionFactory.getOrCreateConn(endpoint); + } + + /** + * Retrieves the HBase connection details from an env variable matching the catalog name, if no such + * env variable exists we fall back to the default env variable defined by DEFAULT_HBASE. + */ + private String getConnStr(MetadataRequest request) + { + String conStr = System.getenv(request.getCatalogName()); + if (conStr == null) { + logger.info("getConnStr: No environment variable found for catalog {} , using default {}", + request.getCatalogName(), DEFAULT_HBASE); + conStr = System.getenv(DEFAULT_HBASE); + } + return conStr; + } + + /** + * List namespaces in your HBase instance treating each as a 'schema' (aka database) + * + * @see GlueMetadataHandler + */ + @Override + public ListSchemasResponse doListSchemaNames(BlockAllocator blockAllocator, ListSchemasRequest request) + throws IOException + { + Connection conn = getOrCreateConn(request); + Admin admin = conn.getAdmin(); + List schemas = new ArrayList<>(); + NamespaceDescriptor[] namespaces = admin.listNamespaceDescriptors(); + for (int i = 0; i < namespaces.length; i++) { + NamespaceDescriptor namespace = namespaces[i]; + schemas.add(namespace.getName()); + } + return new ListSchemasResponse(request.getCatalogName(), schemas); + } + + /** + * List tables in the requested schema in your HBase instance treating the requested schema as an HBase + * namespace. + * + * @see GlueMetadataHandler + */ + @Override + public ListTablesResponse doListTables(BlockAllocator blockAllocator, ListTablesRequest request) + throws IOException + { + Connection conn = getOrCreateConn(request); + Admin admin = conn.getAdmin(); + List tableNames = new ArrayList<>(); + + TableName[] tables = admin.listTableNamesByNamespace(request.getSchemaName()); + for (int i = 0; i < tables.length; i++) { + TableName tableName = tables[i]; + tableNames.add(new com.amazonaws.athena.connector.lambda.domain.TableName(request.getSchemaName(), + tableName.getNameAsString().replace(request.getSchemaName() + ":", ""))); + } + return new ListTablesResponse(request.getCatalogName(), tableNames); + } + + /** + * If Glue is enabled as a source of supplemental metadata we look up the requested Schema/Table in Glue and + * filters out any results that don't have the HBASE_METADATA_FLAG set. If no matching results were found in Glue, + * then we resort to inferring the schema of the HBase table using HbaseSchemaUtils.inferSchema(...). If there + * is no such table in HBase the operation will fail. + * + * @see GlueMetadataHandler + */ + @Override + public GetTableResponse doGetTable(BlockAllocator blockAllocator, GetTableRequest request) + throws Exception + { + logger.info("doGetTable: enter", request.getTableName()); + Schema origSchema = null; + try { + if (awsGlue != null) { + origSchema = super.doGetTable(blockAllocator, request, TABLE_FILTER).getSchema(); + } + } + catch (RuntimeException ex) { + logger.warn("doGetTable: Unable to retrieve table[{}:{}] from AWSGlue.", + request.getTableName().getSchemaName(), + request.getTableName().getTableName(), + ex); + } + + if (origSchema == null) { + Connection conn = getOrCreateConn(request); + origSchema = HbaseSchemaUtils.inferSchema(conn, request.getTableName(), NUM_ROWS_TO_SCAN); + } + + SchemaBuilder schemaBuilder = SchemaBuilder.newBuilder(); + origSchema.getFields().forEach((Field field) -> + schemaBuilder.addField(field.getName(), field.getType(), field.getChildren()) + ); + + origSchema.getCustomMetadata().entrySet().forEach((Map.Entry meta) -> + schemaBuilder.addMetadata(meta.getKey(), meta.getValue())); + + schemaBuilder.addField(HbaseSchemaUtils.ROW_COLUMN_NAME, Types.MinorType.VARCHAR.getType()); + + Schema schema = schemaBuilder.build(); + logger.info("doGetTable: return {}", schema); + return new GetTableResponse(request.getCatalogName(), request.getTableName(), schema); + } + + /** + * Our table doesn't support complex layouts or partitioning so leave this as a NoOp and the SDK will notice that we + * do not have any partition columns, nor have we set an custom fields using enhancePartitionSchema(...), and as a + * result the SDK will generate a single place holder partition for us. This is because we need to convey that there is at least + * 1 partition to read as part of the query or Athena will assume partition pruning found no candidate layouts to read. + * + * @see GlueMetadataHandler + */ + @Override + public void getPartitions(BlockWriter blockWriter, GetTableLayoutRequest request, QueryStatusChecker queryStatusChecker) + { + //NoOp + } + + /** + * If the table is spread across multiple region servers, then we parallelize the scan by making each region server a split. + * + * @see GlueMetadataHandler + */ + @Override + public GetSplitsResponse doGetSplits(BlockAllocator blockAllocator, GetSplitsRequest request) + throws IOException + { + Set splits = new HashSet<>(); + Connection conn = getOrCreateConn(request); + Admin admin = conn.getAdmin(); + + //We can read each region in parallel + for (HRegionInfo info : admin.getTableRegions(HbaseSchemaUtils.getQualifiedTable(request.getTableName()))) { + Split.Builder splitBuilder = Split.newBuilder(makeSpillLocation(request), makeEncryptionKey()) + .add(HBASE_CONN_STR, getConnStr(request)) + .add(START_KEY_FIELD, new String(info.getStartKey())) + .add(END_KEY_FIELD, new String(info.getEndKey())) + .add(REGION_ID_FIELD, String.valueOf(info.getRegionId())) + .add(REGION_NAME_FIELD, info.getRegionNameAsString()); + + splits.add(splitBuilder.build()); + } + + return new GetSplitsResponse(request.getCatalogName(), splits, null); + } + + /** + * @see GlueMetadataHandler + */ + @Override + protected Field convertField(String name, String glueType) + { + return GlueFieldLexer.lex(name, glueType); + } +} diff --git a/athena-hbase/src/main/java/com/amazonaws/athena/connectors/hbase/HbaseRecordHandler.java b/athena-hbase/src/main/java/com/amazonaws/athena/connectors/hbase/HbaseRecordHandler.java new file mode 100644 index 0000000000..1047118ebd --- /dev/null +++ b/athena-hbase/src/main/java/com/amazonaws/athena/connectors/hbase/HbaseRecordHandler.java @@ -0,0 +1,248 @@ +/*- + * #%L + * athena-hbase + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.hbase; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockSpiller; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import com.amazonaws.athena.connector.lambda.handlers.RecordHandler; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.athena.AmazonAthenaClientBuilder; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.amazonaws.services.secretsmanager.AWSSecretsManagerClientBuilder; +import org.apache.arrow.util.VisibleForTesting; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.arrow.vector.types.pojo.Schema; +import org.apache.hadoop.hbase.client.Connection; +import org.apache.hadoop.hbase.client.Result; +import org.apache.hadoop.hbase.client.ResultScanner; +import org.apache.hadoop.hbase.client.Scan; +import org.apache.hadoop.hbase.client.Table; +import org.apache.hadoop.hbase.filter.CompareFilter; +import org.apache.hadoop.hbase.filter.Filter; +import org.apache.hadoop.hbase.filter.SingleColumnValueFilter; +import org.apache.hadoop.hbase.util.Bytes; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.Map; + +import static com.amazonaws.athena.connectors.hbase.HbaseMetadataHandler.END_KEY_FIELD; +import static com.amazonaws.athena.connectors.hbase.HbaseMetadataHandler.HBASE_CONN_STR; +import static com.amazonaws.athena.connectors.hbase.HbaseMetadataHandler.HBASE_NATIVE_STORAGE_FLAG; +import static com.amazonaws.athena.connectors.hbase.HbaseMetadataHandler.START_KEY_FIELD; +import static java.nio.charset.StandardCharsets.UTF_8; + +/** + * Handles data read record requests for the Athena HBase Connector. + *

+ * For more detail, please see the module's README.md, some notable characteristics of this class include: + *

+ * 1. Supporting String and native 'byte[]' storage. + * 2. Attempts to resolve sensitive configuration fields such as HBase connection string via SecretsManager so that you can + * substitute variables with values from by doing something like hostname:port:password=${my_secret} + */ +public class HbaseRecordHandler + extends RecordHandler +{ + private static final Logger logger = LoggerFactory.getLogger(HbaseRecordHandler.class); + + //Used to denote the 'type' of this connector for diagnostic purposes. + private static final String SOURCE_TYPE = "hbase"; + + private final AmazonS3 amazonS3; + private final HbaseConnectionFactory connectionFactory; + + public HbaseRecordHandler() + { + this(AmazonS3ClientBuilder.defaultClient(), + AWSSecretsManagerClientBuilder.defaultClient(), + AmazonAthenaClientBuilder.defaultClient(), + new HbaseConnectionFactory()); + } + + @VisibleForTesting + protected HbaseRecordHandler(AmazonS3 amazonS3, AWSSecretsManager secretsManager, AmazonAthena athena, HbaseConnectionFactory connectionFactory) + { + super(amazonS3, secretsManager, athena, SOURCE_TYPE); + this.amazonS3 = amazonS3; + this.connectionFactory = connectionFactory; + } + + private Connection getOrCreateConn(String conStr) + { + String endpoint = resolveSecrets(conStr); + return connectionFactory.getOrCreateConn(endpoint); + } + + /** + * Scans HBase using the scan settings set on the requested Split by HbaseMetadataHandler. + * + * @see RecordHandler + */ + @Override + protected void readWithConstraint(BlockSpiller blockSpiller, ReadRecordsRequest request, QueryStatusChecker queryStatusChecker) + throws IOException + { + Schema projection = request.getSchema(); + Split split = request.getSplit(); + String conStr = split.getProperty(HBASE_CONN_STR); + boolean isNative = projection.getCustomMetadata().get(HBASE_NATIVE_STORAGE_FLAG) != null; + + //setup the scan so that we only read the key range associated with the region represented by our Split. + Scan scan = new Scan(split.getProperty(START_KEY_FIELD).getBytes(), split.getProperty(END_KEY_FIELD).getBytes()); + + //attempts to push down a partial predicate using HBase Filters + scan.setFilter(pushdownPredicate(isNative, request.getConstraints())); + + //setup the projection so we only pull columns/families that we need + for (Field next : request.getSchema().getFields()) { + addToProjection(scan, next); + } + + Connection conn = getOrCreateConn(conStr); + Table table = conn.getTable(HbaseSchemaUtils.getQualifiedTable(request.getTableName())); + + try (ResultScanner scanner = table.getScanner(scan)) { + for (Result row : scanner) { + if (!queryStatusChecker.isQueryRunning()) { + return; + } + blockSpiller.writeRows((Block block, int rowNum) -> { + boolean match = true; + for (Field field : projection.getFields()) { + if (match) { + match &= writeField(block, field, isNative, row, rowNum); + } + } + return match ? 1 : 0; + }); + } + } + catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + /** + * Used to filter and write field values from the HBase scan to the response block. + * + * @param block The Block we should write to. + * @param field The Apache Arrow Field we need to write. + * @param isNative Boolean indicating if the HBase value is stored as a String (false) or as Native byte[] (true). + * @param row The HBase row from which we should extract a value for the field denoted by vector. + * @param rowNum The rowNumber to write into on the vector. + * @return True if the value passed the ConstraintEvaluator's test. + */ + private boolean writeField(Block block, Field field, boolean isNative, Result row, int rowNum) + { + String fieldName = field.getName(); + ArrowType type = field.getType(); + Types.MinorType minorType = Types.getMinorTypeForArrowType(type); + try { + //Is this field the special 'row' field that can be used to group column families that may + //have been spread across different region servers if they are needed in the same query. + if (HbaseSchemaUtils.ROW_COLUMN_NAME.equals(fieldName)) { + String value = Bytes.toString(row.getRow()); + return block.offerValue(fieldName, rowNum, value); + } + + switch (minorType) { + case STRUCT: + //Column is actually a Column Family stored as a STRUCT. + return block.offerComplexValue(fieldName, + rowNum, + HbaseFieldResolver.resolver(isNative, fieldName), + row); + default: + //We expect the column name format to be : + String[] columnParts = HbaseSchemaUtils.extractColumnParts(fieldName); + byte[] rawValue = row.getValue(columnParts[0].getBytes(), columnParts[1].getBytes()); + Object value = HbaseSchemaUtils.coerceType(isNative, type, rawValue); + return block.offerValue(fieldName, rowNum, value); + } + } + catch (RuntimeException ex) { + throw new RuntimeException("Exception while processing field " + fieldName + " type " + minorType, ex); + } + } + + /** + * Addes the specified Apache Arrow field to the Scan to satisfy the requested projection. + * + * @param scan The scan object that will be used to read data from HBase. + * @param field The field to be added to the scan. + */ + private void addToProjection(Scan scan, Field field) + { + //ignore the special 'row' column since we get that by default. + if (HbaseSchemaUtils.ROW_COLUMN_NAME.equalsIgnoreCase(field.getName())) { + return; + } + + Types.MinorType columnType = Types.getMinorTypeForArrowType(field.getType()); + switch (columnType) { + case STRUCT: + for (Field child : field.getChildren()) { + scan.addColumn(field.getName().getBytes(UTF_8), child.getName().getBytes(UTF_8)); + } + return; + default: + String[] nameParts = HbaseSchemaUtils.extractColumnParts(field.getName()); + if (nameParts.length != 2) { + throw new RuntimeException("Column name " + field.getName() + " does not meet family:column hbase convention."); + } + scan.addColumn(nameParts[0].getBytes(UTF_8), nameParts[1].getBytes(UTF_8)); + } + } + + /** + * Attempts to push down at basic Filter predicate into HBase. + * + * @param isNative True if the values are stored in HBase using native byte[] vs being serialized as Strings. + * @param constraints The constraints that we can attempt to push into HBase as part of the scan. + * @return A filter if we found a predicate we can push down, null otherwise/ + * @note Currently this method only supports constraints that can be represented by HBase's SingleColumnValueFilter + * and CompareOp of EQUAL. In the future we can add > and < for certain field types. + */ + private Filter pushdownPredicate(boolean isNative, Constraints constraints) + { + for (Map.Entry next : constraints.getSummary().entrySet()) { + if (next.getValue().isSingleValue() && !next.getValue().isNullAllowed()) { + String[] colParts = HbaseSchemaUtils.extractColumnParts(next.getKey()); + return new SingleColumnValueFilter(colParts[0].getBytes(), + colParts[1].getBytes(), + CompareFilter.CompareOp.EQUAL, + HbaseSchemaUtils.toBytes(isNative, next.getValue().getSingleValue())); + } + } + + return null; + } +} diff --git a/athena-hbase/src/main/java/com/amazonaws/athena/connectors/hbase/HbaseSchemaUtils.java b/athena-hbase/src/main/java/com/amazonaws/athena/connectors/hbase/HbaseSchemaUtils.java new file mode 100644 index 0000000000..96926cc2a8 --- /dev/null +++ b/athena-hbase/src/main/java/com/amazonaws/athena/connectors/hbase/HbaseSchemaUtils.java @@ -0,0 +1,277 @@ +/*- + * #%L + * athena-hbase + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.hbase; + +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.arrow.vector.types.pojo.Schema; +import org.apache.arrow.vector.util.Text; +import org.apache.hadoop.hbase.KeyValue; +import org.apache.hadoop.hbase.client.Connection; +import org.apache.hadoop.hbase.client.Result; +import org.apache.hadoop.hbase.client.ResultScanner; +import org.apache.hadoop.hbase.client.Scan; +import org.apache.hadoop.hbase.client.Table; +import org.apache.hadoop.hbase.filter.PageFilter; +import org.apache.hadoop.hbase.util.Bytes; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; + +/** + * Collection of helpful utilities that handle HBase schema inference, type, and naming conversion. + */ +public class HbaseSchemaUtils +{ + //Field name for the special 'row' column which represets the HBase key used to store a given row. + protected static final String ROW_COLUMN_NAME = "row"; + //The HBase namespce qualifier character which commonly separates namespaces and column families from tables and columns. + protected static final String NAMESPACE_QUALIFIER = ":"; + private static final Logger logger = LoggerFactory.getLogger(HbaseSchemaUtils.class); + + private HbaseSchemaUtils() {} + + /** + * This method will produce an Apache Arrow Schema for the given TableName and HBase connection + * by scanning up to the requested number of rows and using basic schema inference to determine + * data types. + * + * @param client The HBase connection to use for the scan operation. + * @param tableName The HBase TableName for which to produce an Apache Arrow Schema. + * @param numToScan The number of records to scan as part of producing the Schema. + * @return An Apache Arrow Schema representing the schema of the HBase table. + * @note The resulting schema is a union of the schema of every row that is scanned. Any time two rows + * have a field with the same name but different inferred type the code will default the type of + * that field in the resulting schema to a VARCHAR. This approach is not perfect and can struggle + * to produce a usable schema if the table has a significant mix of entities. + */ + public static Schema inferSchema(Connection client, TableName tableName, int numToScan) + { + Map> schemaInference = new HashMap<>(); + Scan scan = new Scan().setMaxResultSize(numToScan).setFilter(new PageFilter(numToScan)); + try (Table table = client.getTable(org.apache.hadoop.hbase.TableName.valueOf(getQualifiedTableName(tableName))); + ResultScanner scanner = table.getScanner(scan)) { + for (Result result : scanner) { + for (KeyValue keyValue : result.list()) { + String family = new String(keyValue.getFamily()); + String column = new String(keyValue.getQualifier()); + + Map schemaForFamily = schemaInference.get(family); + if (schemaForFamily == null) { + schemaForFamily = new HashMap<>(); + schemaInference.put(family, schemaForFamily); + } + + //Get the previously inferred type for this column if we've seen it on a past row + ArrowType prevInferredType = schemaForFamily.get(column); + + //Infer the type of the column from the value on the current row. + Types.MinorType inferredType = inferType(keyValue.getValue()); + + //Check if the previous and currently inferred types match + if (prevInferredType != null && Types.getMinorTypeForArrowType(prevInferredType) != inferredType) { + logger.info("inferSchema: Type changed detected for field, using VARCHAR - family: {} col: {} previousType: {} newType: {}", + family, column, prevInferredType, inferredType); + schemaForFamily.put(column, Types.MinorType.VARCHAR.getType()); + } + else { + schemaForFamily.put(column, inferredType.getType()); + } + + logger.info("inferSchema: family: {} col: {} inferredType: {}", family, column, inferredType); + } + } + + //Used the union of all row's to produce our resultant Apache Arrow Schema. + SchemaBuilder schemaBuilder = SchemaBuilder.newBuilder(); + for (Map.Entry> nextFamily : schemaInference.entrySet()) { + String family = nextFamily.getKey(); + for (Map.Entry nextCol : nextFamily.getValue().entrySet()) { + schemaBuilder.addField(family + NAMESPACE_QUALIFIER + nextCol.getKey(), nextCol.getValue()); + } + } + + return schemaBuilder.build(); + } + catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + /** + * Helper which goes from an Athena Federation SDK TableName to an HBase table name string. + * + * @param tableName An Athena Federation SDK TableName. + * @return The corresponding HBase table name string. + */ + public static String getQualifiedTableName(TableName tableName) + { + return tableName.getSchemaName() + NAMESPACE_QUALIFIER + tableName.getTableName(); + } + + /** + * Helper which goes from an Athena Federation SDK TableName to an HBase TableName. + * + * @param tableName An Athena Federation SDK TableName. + * @return The corresponding HBase TableName. + */ + public static org.apache.hadoop.hbase.TableName getQualifiedTable(TableName tableName) + { + return org.apache.hadoop.hbase.TableName.valueOf(tableName.getSchemaName() + NAMESPACE_QUALIFIER + tableName.getTableName()); + } + + /** + * Given a value from HBase attempt to infer it's type. + * + * @param value An HBase value. + * @return The Apache Arrow Minor Type most closely associated with the provided value. + * @note This method of inference is very naive and only works if the values are stored in HBase + * as Strings. It uses VARCHAR as its fallback if it can't not parse our the value to + * one of the other supported inferred types. It is expected that customers of this connector + * may want to customize this logic or rely on explicit Schema in Glue. + */ + public static Types.MinorType inferType(byte[] value) + { + String strVal = Bytes.toString(value); + try { + Long.valueOf(strVal); + return Types.MinorType.BIGINT; + } + catch (RuntimeException ex) { + } + + try { + Double.valueOf(strVal); + return Types.MinorType.FLOAT8; + } + catch (RuntimeException ex) { + } + + return Types.MinorType.VARCHAR; + } + + /** + * Helper that can coerce the given HBase value to the requested Apache Arrow type. + * + * @param isNative If True, the HBase value is stored using native bytes. If False, the value is serialized as a String. + * @param type The Apache Arrow Type that the value should be coerced to before returning. + * @param value The HBase value to coerce. + * @return The coerced value which is now allowed with the provided Apache Arrow type. + */ + public static Object coerceType(boolean isNative, ArrowType type, byte[] value) + { + if (value == null) { + return null; + } + + Types.MinorType minorType = Types.getMinorTypeForArrowType(type); + switch (minorType) { + case VARCHAR: + return Bytes.toString(value); + case INT: + return isNative ? ByteBuffer.wrap(value).getInt() : Integer.parseInt(Bytes.toString(value)); + case BIGINT: + return isNative ? ByteBuffer.wrap(value).getLong() : Long.parseLong(Bytes.toString(value)); + case FLOAT4: + return isNative ? ByteBuffer.wrap(value).getFloat() : Float.parseFloat(Bytes.toString(value)); + case FLOAT8: + return isNative ? ByteBuffer.wrap(value).getDouble() : Double.parseDouble(Bytes.toString(value)); + case BIT: + if (isNative) { + return (value[0] != 0); + } + else { + return Boolean.parseBoolean(Bytes.toString(value)); + } + case VARBINARY: + return value; + default: + throw new IllegalArgumentException(type + " with minorType[" + minorType + "] is not supported."); + } + } + + /** + * Helper which can go from a Glue/Apache Arrow column name to its HBase family + column. + * + * @param glueColumnName The input column name in format "family:column". + * @return + */ + public static String[] extractColumnParts(String glueColumnName) + { + return glueColumnName.split(NAMESPACE_QUALIFIER); + } + + /** + * Used to convert from Apache Arrow typed values to HBase values. + * + * @param isNative If True, the HBase value should be stored using native bytes. + * If False, the value should be serialized as a String before storing it. + * @param value The value to convert. + * @return The HBase byte representation of the value. + * @note This is commonly used when attempting to push constraints into HBase which requires converting a small + * number of values from Apache Arrow's Type system to HBase compatible representations for comparisons. + */ + public static byte[] toBytes(boolean isNative, Object value) + { + if (value == null || value instanceof byte[]) { + return (byte[]) value; + } + + if (value instanceof String) { + return ((String) value).getBytes(); + } + + if (value instanceof Text) { + return ((Text) value).toString().getBytes(); + } + + if (!isNative) { + return String.valueOf(value).getBytes(); + } + + if (value instanceof Integer) { + return ByteBuffer.allocate(4).putInt((int) value).array(); + } + + if (value instanceof Long) { + return ByteBuffer.allocate(8).putLong((long) value).array(); + } + + if (value instanceof Float) { + return ByteBuffer.allocate(4).putFloat((float) value).array(); + } + + if (value instanceof Double) { + return ByteBuffer.allocate(8).putDouble((double) value).array(); + } + + if (value instanceof Boolean) { + return ByteBuffer.allocate(1).put((byte) ((boolean) value ? 1 : 0)).array(); + } + + throw new RuntimeException("Unsupported object type for " + value + " " + value.getClass().getName()); + } +} diff --git a/athena-hbase/src/main/resources/sample_data.hbase b/athena-hbase/src/main/resources/sample_data.hbase new file mode 100644 index 0000000000..45ecdfb360 --- /dev/null +++ b/athena-hbase/src/main/resources/sample_data.hbase @@ -0,0 +1,106 @@ + +create 'hbase_payments:transactions', 'summary','details' + +put 'hbase_payments:transactions','tx00001','summary:amount',1810.21 +put 'hbase_payments:transactions','tx00001','summary:cc_id','4119' +put 'hbase_payments:transactions','tx00001','summary:auth','XDF6J' +put 'hbase_payments:transactions','tx00001','summary:status','FUNDED' +put 'hbase_payments:transactions','tx00001','summary:order_id','0001235' +put 'hbase_payments:transactions','tx00001','summary:customer_id','11123' +put 'hbase_payments:transactions','tx00001','details:fee',18.02 +put 'hbase_payments:transactions','tx00001','details:bank','AMEX' +put 'hbase_payments:transactions','tx00001','details:network','AMEX' +put 'hbase_payments:transactions','tx00001','details:days_payable',30 +put 'hbase_payments:transactions','tx00001','details:latency',450 +put 'hbase_payments:transactions','tx00001','details:fraud_score',1 + + +put 'hbase_payments:transactions','tx00002','summary:amount',110.11 +put 'hbase_payments:transactions','tx00002','summary:cc_id','4119' +put 'hbase_payments:transactions','tx00002','summary:auth','OKLH8' +put 'hbase_payments:transactions','tx00002','summary:status','FUNDED' +put 'hbase_payments:transactions','tx00002','summary:order_id','0001234' +put 'hbase_payments:transactions','tx00002','summary:customer_id','11123' +put 'hbase_payments:transactions','tx00002','details:fee',1.10 +put 'hbase_payments:transactions','tx00002','details:bank','AMEX' +put 'hbase_payments:transactions','tx00002','details:network','AMEX' +put 'hbase_payments:transactions','tx00002','details:days_payable',30 +put 'hbase_payments:transactions','tx00002','details:latency',350 +put 'hbase_payments:transactions','tx00002','details:fraud_score',2 + + +put 'hbase_payments:transactions','tx00003','summary:amount',33.12 +put 'hbase_payments:transactions','tx00003','summary:cc_id','1189' +put 'hbase_payments:transactions','tx00003','summary:auth','OKLH8' +put 'hbase_payments:transactions','tx00003','summary:status','PENDING' +put 'hbase_payments:transactions','tx00003','summary:order_id','0002234' +put 'hbase_payments:transactions','tx00003','summary:customer_id','9820' +put 'hbase_payments:transactions','tx00003','details:fee',0.33 +put 'hbase_payments:transactions','tx00003','details:bank','CHASE' +put 'hbase_payments:transactions','tx00003','details:network','VISA' +put 'hbase_payments:transactions','tx00003','details:days_payable',90 +put 'hbase_payments:transactions','tx00003','details:latency',800 +put 'hbase_payments:transactions','tx00003','details:fraud_score',7 + +put 'hbase_payments:transactions','tx00004','summary:amount',323.82 +put 'hbase_payments:transactions','tx00004','summary:cc_id','8827' +put 'hbase_payments:transactions','tx00004','summary:auth','8UJKZS' +put 'hbase_payments:transactions','tx00004','summary:status','FUNDED' +put 'hbase_payments:transactions','tx00004','summary:order_id','0001238' +put 'hbase_payments:transactions','tx00004','summary:customer_id','453' +put 'hbase_payments:transactions','tx00004','details:fee',3.23 +put 'hbase_payments:transactions','tx00004','details:bank','BoA' +put 'hbase_payments:transactions','tx00004','details:network','MASTERCARD' +put 'hbase_payments:transactions','tx00004','details:days_payable',45 +put 'hbase_payments:transactions','tx00004','details:latency',600 +put 'hbase_payments:transactions','tx00004','details:fraud_score',3 + +put 'hbase_payments:transactions','tx00005','summary:amount',8.57 +put 'hbase_payments:transactions','tx00005','summary:cc_id','9001' +put 'hbase_payments:transactions','tx00005','summary:auth','PLQA2' +put 'hbase_payments:transactions','tx00005','summary:status','PENDING' +put 'hbase_payments:transactions','tx00005','summary:order_id','0001237' +put 'hbase_payments:transactions','tx00005','summary:customer_id','92053' +put 'hbase_payments:transactions','tx00005','details:fee',0.08 +put 'hbase_payments:transactions','tx00005','details:bank','CHASE' +put 'hbase_payments:transactions','tx00005','details:network','VISA' +put 'hbase_payments:transactions','tx00005','details:days_payable',90 +put 'hbase_payments:transactions','tx00005','details:latency',250 +put 'hbase_payments:transactions','tx00005','details:fraud_score',2 + +put 'hbase_payments:transactions','tx00006','summary:amount',18.10 +put 'hbase_payments:transactions','tx00006','summary:cc_id','5612' +put 'hbase_payments:transactions','tx00006','summary:auth','BVF32' +put 'hbase_payments:transactions','tx00006','summary:status','PENDING' +put 'hbase_payments:transactions','tx00006','summary:order_id','0001236' +put 'hbase_payments:transactions','tx00006','summary:customer_id','12151' +put 'hbase_payments:transactions','tx00006','details:fee',0.01 +put 'hbase_payments:transactions','tx00006','details:bank','CHASE' +put 'hbase_payments:transactions','tx00006','details:network','VISA' +put 'hbase_payments:transactions','tx00006','details:days_payable',90 +put 'hbase_payments:transactions','tx00006','details:latency',112 +put 'hbase_payments:transactions','tx00006','details:fraud_score',1 + +scan 'hbase_payments:transactions' + +create 'hbase_payments:payment_providers', 'provider','network' + +put 'hbase_payments:payment_providers','VISA','provider:name','VISA' +put 'hbase_payments:payment_providers','VISA','provider:fee',0.01 +put 'hbase_payments:payment_providers','VISA','network:type','atm' +put 'hbase_payments:payment_providers','VISA','network:endpoint','visa.clearing.com' +put 'hbase_payments:payment_providers','VISA','network:endpoint_port',8080 + +put 'hbase_payments:payment_providers','MASTERCARD','provider:name','MASTERCARD' +put 'hbase_payments:payment_providers','MASTERCARD','provider:fee',0.02 +put 'hbase_payments:payment_providers','MASTERCARD','network:type','json-rpc' +put 'hbase_payments:payment_providers','MASTERCARD','network:endpoint','ms.ms-clearing.com' +put 'hbase_payments:payment_providers','MASTERCARD','network:endpoint_port',443 + +put 'hbase_payments:payment_providers','AMEX','provider:name','AMEX' +put 'hbase_payments:payment_providers','AMEX','provider:fee',0.03 +put 'hbase_payments:payment_providers','AMEX','network:type','xml-rpc' +put 'hbase_payments:payment_providers','AMEX','network:endpoint','amex.amex-clearing.com' +put 'hbase_payments:payment_providers','AMEX','network:endpoint_port',443 + +scan 'hbase_payments:payment_providers' \ No newline at end of file diff --git a/athena-hbase/src/test/java/com/amazonaws/athena/connectors/hbase/HbaseConnectionFactoryTest.java b/athena-hbase/src/test/java/com/amazonaws/athena/connectors/hbase/HbaseConnectionFactoryTest.java new file mode 100644 index 0000000000..2c5622b658 --- /dev/null +++ b/athena-hbase/src/test/java/com/amazonaws/athena/connectors/hbase/HbaseConnectionFactoryTest.java @@ -0,0 +1,62 @@ +/*- + * #%L + * athena-hbase + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.hbase; + +import org.apache.hadoop.hbase.client.Admin; +import org.apache.hadoop.hbase.client.Connection; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class HbaseConnectionFactoryTest +{ + private HbaseConnectionFactory connectionFactory; + + @Before + public void setUp() + throws Exception + { + connectionFactory = new HbaseConnectionFactory(); + } + + @Test + public void clientCacheHitTest() + throws IOException + { + Connection mockConn = mock(Connection.class); + Admin mockAdmin = mock(Admin.class); + when(mockConn.getAdmin()).thenReturn(mockAdmin); + + connectionFactory.addConnection("conStr", mockConn); + Connection conn = connectionFactory.getOrCreateConn("conStr"); + + assertEquals(mockConn, conn); + verify(mockConn, times(1)).getAdmin(); + verify(mockAdmin, times(1)).listTableNames(); + } +} diff --git a/athena-hbase/src/test/java/com/amazonaws/athena/connectors/hbase/HbaseFieldResolverTest.java b/athena-hbase/src/test/java/com/amazonaws/athena/connectors/hbase/HbaseFieldResolverTest.java new file mode 100644 index 0000000000..62360e609a --- /dev/null +++ b/athena-hbase/src/test/java/com/amazonaws/athena/connectors/hbase/HbaseFieldResolverTest.java @@ -0,0 +1,49 @@ +/*- + * #%L + * athena-hbase + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.hbase; + +import com.amazonaws.athena.connector.lambda.data.FieldBuilder; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.hadoop.hbase.client.Result; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class HbaseFieldResolverTest +{ + + @Test + public void getFieldValue() + { + String expectedValue = "myValue"; + String family = "family"; + Field field = FieldBuilder.newBuilder("field1", Types.MinorType.VARCHAR.getType()).build(); + Result mockResult = mock(Result.class); + HbaseFieldResolver resolver = HbaseFieldResolver.resolver(false, family); + + when(mockResult.getValue(any(byte[].class), any(byte[].class))).thenReturn(expectedValue.getBytes()); + Object result = resolver.getFieldValue(field, mockResult); + assertEquals(expectedValue, result); + } +} diff --git a/athena-hbase/src/test/java/com/amazonaws/athena/connectors/hbase/HbaseMetadataHandlerTest.java b/athena-hbase/src/test/java/com/amazonaws/athena/connectors/hbase/HbaseMetadataHandlerTest.java new file mode 100644 index 0000000000..290e57a3bb --- /dev/null +++ b/athena-hbase/src/test/java/com/amazonaws/athena/connectors/hbase/HbaseMetadataHandlerTest.java @@ -0,0 +1,301 @@ +/*- + * #%L + * athena-hbase + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.hbase; + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.amazonaws.athena.connector.lambda.data.BlockUtils; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesResponse; +import com.amazonaws.athena.connector.lambda.metadata.MetadataRequestType; +import com.amazonaws.athena.connector.lambda.metadata.MetadataResponse; +import com.amazonaws.athena.connector.lambda.security.FederatedIdentity; +import com.amazonaws.athena.connector.lambda.security.LocalKeyFactory; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.glue.AWSGlue; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.arrow.vector.types.pojo.Schema; +import org.apache.hadoop.hbase.HRegionInfo; +import org.apache.hadoop.hbase.NamespaceDescriptor; +import org.apache.hadoop.hbase.client.Admin; +import org.apache.hadoop.hbase.client.Connection; +import org.apache.hadoop.hbase.client.Result; +import org.apache.hadoop.hbase.client.ResultScanner; +import org.apache.hadoop.hbase.client.Scan; +import org.apache.hadoop.hbase.client.Table; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.runners.MockitoJUnitRunner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static com.amazonaws.athena.connectors.hbase.TestUtils.makeResult; +import static org.junit.Assert.*; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class HbaseMetadataHandlerTest +{ + private static final Logger logger = LoggerFactory.getLogger(HbaseMetadataHandlerTest.class); + + private FederatedIdentity identity = new FederatedIdentity("id", "principal", "account"); + private String catalog = "default"; + private HbaseMetadataHandler handler; + private BlockAllocator allocator; + + @Mock + private Connection mockClient; + + @Mock + private Admin mockAdmin; + + @Mock + private Table mockTable; + + @Mock + private HbaseConnectionFactory mockConnFactory; + + @Mock + private AWSGlue awsGlue; + + @Mock + private AWSSecretsManager secretsManager; + + @Mock + private AmazonAthena athena; + + @Before + public void setUp() + throws Exception + { + handler = new HbaseMetadataHandler(awsGlue, + new LocalKeyFactory(), + secretsManager, + athena, + mockConnFactory, + "spillBucket", + "spillPrefix"); + + when(mockConnFactory.getOrCreateConn(anyString())).thenReturn(mockClient); + when(mockClient.getAdmin()).thenReturn(mockAdmin); + when(mockClient.getTable(any())).thenReturn(mockTable); + + allocator = new BlockAllocatorImpl(); + } + + @After + public void tearDown() + throws Exception + { + allocator.close(); + } + + @Test + public void doListSchemaNames() + throws IOException + { + logger.info("doListSchemaNames: enter"); + + NamespaceDescriptor[] schemaNames = {NamespaceDescriptor.create("schema1").build(), + NamespaceDescriptor.create("schema2").build(), + NamespaceDescriptor.create("schema3").build()}; + + when(mockAdmin.listNamespaceDescriptors()).thenReturn(schemaNames); + + ListSchemasRequest req = new ListSchemasRequest(identity, "queryId", "default"); + ListSchemasResponse res = handler.doListSchemaNames(allocator, req); + + logger.info("doListSchemas - {}", res.getSchemas()); + Set expectedSchemaName = new HashSet<>(); + expectedSchemaName.add("schema1"); + expectedSchemaName.add("schema2"); + expectedSchemaName.add("schema3"); + assertEquals(expectedSchemaName, new HashSet<>(res.getSchemas())); + + logger.info("doListSchemaNames: exit"); + } + + @Test + public void doListTables() + throws IOException + { + logger.info("doListTables - enter"); + + String schema = "schema1"; + + org.apache.hadoop.hbase.TableName[] tables = { + org.apache.hadoop.hbase.TableName.valueOf("schema1", "table1"), + org.apache.hadoop.hbase.TableName.valueOf("schema1", "table2"), + org.apache.hadoop.hbase.TableName.valueOf("schema1", "table3") + }; + + Set tableNames = new HashSet<>(); + tableNames.add("table1"); + tableNames.add("table2"); + tableNames.add("table3"); + + when(mockAdmin.listTableNamesByNamespace(eq(schema))).thenReturn(tables); + ListTablesRequest req = new ListTablesRequest(identity, "queryId", "default", schema); + ListTablesResponse res = handler.doListTables(allocator, req); + logger.info("doListTables - {}", res.getTables()); + + for (TableName next : res.getTables()) { + assertEquals(schema, next.getSchemaName()); + assertTrue(tableNames.contains(next.getTableName())); + } + assertEquals(tableNames.size(), res.getTables().size()); + + logger.info("doListTables - exit"); + } + + /** + * TODO: Add more types. + */ + @Test + public void doGetTable() + throws Exception + { + logger.info("doGetTable - enter"); + + String schema = "schema1"; + String table = "table1"; + List results = TestUtils.makeResults(); + + ResultScanner mockScanner = mock(ResultScanner.class); + when(mockTable.getScanner(any(Scan.class))).thenReturn(mockScanner); + when(mockScanner.iterator()).thenReturn(results.iterator()); + + GetTableRequest req = new GetTableRequest(identity, "queryId", catalog, new TableName(schema, table)); + GetTableResponse res = handler.doGetTable(allocator, req); + logger.info("doGetTable - {}", res); + + Schema expectedSchema = TestUtils.makeSchema() + .addField(HbaseSchemaUtils.ROW_COLUMN_NAME, Types.MinorType.VARCHAR.getType()) + .build(); + + assertEquals(expectedSchema.getFields().size(), res.getSchema().getFields().size()); + logger.info("doGetTable - exit"); + } + + @Test + public void doGetTableLayout() + throws Exception + { + logger.info("doGetTableLayout - enter"); + + GetTableLayoutRequest req = new GetTableLayoutRequest(identity, + "queryId", + "default", + new TableName("schema1", "table1"), + new Constraints(new HashMap<>()), + SchemaBuilder.newBuilder().build(), + Collections.EMPTY_SET); + + GetTableLayoutResponse res = handler.doGetTableLayout(allocator, req); + + logger.info("doGetTableLayout - {}", res); + Block partitions = res.getPartitions(); + for (int row = 0; row < partitions.getRowCount() && row < 10; row++) { + logger.info("doGetTableLayout:{} {}", row, BlockUtils.rowToString(partitions, row)); + } + + assertTrue(partitions.getRowCount() > 0); + + logger.info("doGetTableLayout: partitions[{}]", partitions.getRowCount()); + } + + @Test + public void doGetSplits() + throws IOException + { + logger.info("doGetSplits: enter"); + + List regionServers = new ArrayList<>(); + regionServers.add(TestUtils.makeRegion(1, "schema1", "table1")); + regionServers.add(TestUtils.makeRegion(2, "schema1", "table1")); + regionServers.add(TestUtils.makeRegion(3, "schema1", "table1")); + regionServers.add(TestUtils.makeRegion(4, "schema1", "table1")); + + when(mockAdmin.getTableRegions(any())).thenReturn(regionServers); + List partitionCols = new ArrayList<>(); + + Block partitions = BlockUtils.newBlock(allocator, "partitionId", Types.MinorType.INT.getType(), 0); + + String continuationToken = null; + GetSplitsRequest originalReq = new GetSplitsRequest(identity, + "queryId", + "catalog_name", + new TableName("schema", "table_name"), + partitions, + partitionCols, + new Constraints(new HashMap<>()), + null); + + GetSplitsRequest req = new GetSplitsRequest(originalReq, continuationToken); + + logger.info("doGetSplits: req[{}]", req); + + MetadataResponse rawResponse = handler.doGetSplits(allocator, req); + assertEquals(MetadataRequestType.GET_SPLITS, rawResponse.getRequestType()); + + GetSplitsResponse response = (GetSplitsResponse) rawResponse; + continuationToken = response.getContinuationToken(); + + logger.info("doGetSplits: continuationToken[{}] - numSplits[{}]", + new Object[] {continuationToken, response.getSplits().size()}); + + assertTrue("Continuation criteria violated", response.getSplits().size() == 4); + assertTrue("Continuation criteria violated", response.getContinuationToken() == null); + + logger.info("doGetSplits: exit"); + } +} diff --git a/athena-hbase/src/test/java/com/amazonaws/athena/connectors/hbase/HbaseRecordHandlerTest.java b/athena-hbase/src/test/java/com/amazonaws/athena/connectors/hbase/HbaseRecordHandlerTest.java new file mode 100644 index 0000000000..a5132ec1bc --- /dev/null +++ b/athena-hbase/src/test/java/com/amazonaws/athena/connectors/hbase/HbaseRecordHandlerTest.java @@ -0,0 +1,304 @@ +/*- + * #%L + * athena-hbase + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.hbase; + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.amazonaws.athena.connector.lambda.data.BlockUtils; +import com.amazonaws.athena.connector.lambda.data.S3BlockSpillReader; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.domain.predicate.Range; +import com.amazonaws.athena.connector.lambda.domain.predicate.SortedRangeSet; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import com.amazonaws.athena.connector.lambda.domain.spill.S3SpillLocation; +import com.amazonaws.athena.connector.lambda.domain.spill.SpillLocation; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsResponse; +import com.amazonaws.athena.connector.lambda.records.RecordResponse; +import com.amazonaws.athena.connector.lambda.records.RemoteReadRecordsResponse; +import com.amazonaws.athena.connector.lambda.security.EncryptionKeyFactory; +import com.amazonaws.athena.connector.lambda.security.FederatedIdentity; +import com.amazonaws.athena.connector.lambda.security.LocalKeyFactory; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.PutObjectResult; +import com.amazonaws.services.s3.model.S3Object; +import com.amazonaws.services.s3.model.S3ObjectInputStream; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.google.common.collect.ImmutableList; +import com.google.common.io.ByteStreams; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Schema; +import org.apache.hadoop.hbase.client.Connection; +import org.apache.hadoop.hbase.client.Result; +import org.apache.hadoop.hbase.client.ResultScanner; +import org.apache.hadoop.hbase.client.Scan; +import org.apache.hadoop.hbase.client.Table; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.runners.MockitoJUnitRunner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import static com.amazonaws.athena.connectors.hbase.HbaseMetadataHandler.END_KEY_FIELD; +import static com.amazonaws.athena.connectors.hbase.HbaseMetadataHandler.HBASE_CONN_STR; +import static com.amazonaws.athena.connectors.hbase.HbaseMetadataHandler.REGION_ID_FIELD; +import static com.amazonaws.athena.connectors.hbase.HbaseMetadataHandler.REGION_NAME_FIELD; +import static com.amazonaws.athena.connectors.hbase.HbaseMetadataHandler.START_KEY_FIELD; +import static org.junit.Assert.*; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class HbaseRecordHandlerTest +{ + private static final Logger logger = LoggerFactory.getLogger(HbaseRecordHandlerTest.class); + + private FederatedIdentity identity = new FederatedIdentity("id", "principal", "account"); + private String catalog = "default"; + private HbaseRecordHandler handler; + private BlockAllocator allocator; + private List mockS3Storage = new ArrayList<>(); + private AmazonS3 amazonS3; + private S3BlockSpillReader spillReader; + private Schema schemaForRead; + private EncryptionKeyFactory keyFactory = new LocalKeyFactory(); + + @Mock + private Connection mockClient; + + @Mock + private Table mockTable; + + @Mock + private HbaseConnectionFactory mockConnFactory; + + @Mock + private AWSSecretsManager mockSecretsManager; + + @Mock + private AmazonAthena mockAthena; + + @Before + public void setUp() + throws IOException + { + logger.info("setUpBefore - enter"); + + when(mockConnFactory.getOrCreateConn(anyString())).thenReturn(mockClient); + when(mockClient.getTable(any())).thenReturn(mockTable); + + allocator = new BlockAllocatorImpl(); + + amazonS3 = mock(AmazonS3.class); + + when(amazonS3.putObject(anyObject(), anyObject(), anyObject(), anyObject())) + .thenAnswer((InvocationOnMock invocationOnMock) -> { + InputStream inputStream = (InputStream) invocationOnMock.getArguments()[2]; + ByteHolder byteHolder = new ByteHolder(); + byteHolder.setBytes(ByteStreams.toByteArray(inputStream)); + mockS3Storage.add(byteHolder); + return mock(PutObjectResult.class); + }); + + when(amazonS3.getObject(anyString(), anyString())) + .thenAnswer((InvocationOnMock invocationOnMock) -> { + S3Object mockObject = mock(S3Object.class); + ByteHolder byteHolder = mockS3Storage.get(0); + mockS3Storage.remove(0); + when(mockObject.getObjectContent()).thenReturn( + new S3ObjectInputStream( + new ByteArrayInputStream(byteHolder.getBytes()), null)); + return mockObject; + }); + + schemaForRead = TestUtils.makeSchema().addStringField(HbaseSchemaUtils.ROW_COLUMN_NAME).build(); + + handler = new HbaseRecordHandler(amazonS3, mockSecretsManager, mockAthena, mockConnFactory); + spillReader = new S3BlockSpillReader(amazonS3, allocator); + + logger.info("setUpBefore - exit"); + } + + @After + public void after() + { + allocator.close(); + } + + @Test + public void doReadRecordsNoSpill() + throws Exception + { + logger.info("doReadRecordsNoSpill: enter"); + + String schema = "schema1"; + String table = "table1"; + + List results = TestUtils.makeResults(100); + ResultScanner mockScanner = mock(ResultScanner.class); + when(mockTable.getScanner(any(Scan.class))).thenReturn(mockScanner); + when(mockScanner.iterator()).thenReturn(results.iterator()); + + Map constraintsMap = new HashMap<>(); + constraintsMap.put("family1:col3", SortedRangeSet.copyOf(Types.MinorType.BIGINT.getType(), + ImmutableList.of(Range.equal(allocator, Types.MinorType.BIGINT.getType(), 1L)), false)); + + S3SpillLocation splitLoc = S3SpillLocation.newBuilder() + .withBucket(UUID.randomUUID().toString()) + .withSplitId(UUID.randomUUID().toString()) + .withQueryId(UUID.randomUUID().toString()) + .withIsDirectory(true) + .build(); + + Split.Builder splitBuilder = Split.newBuilder(splitLoc, keyFactory.create()) + .add(HBASE_CONN_STR, "fake_con_str") + .add(START_KEY_FIELD, "fake_start_key") + .add(END_KEY_FIELD, "fake_end_key") + .add(REGION_ID_FIELD, "fake_region_id") + .add(REGION_NAME_FIELD, "fake_region_name"); + + ReadRecordsRequest request = new ReadRecordsRequest(identity, + catalog, + "queryId-" + System.currentTimeMillis(), + new TableName(schema, table), + schemaForRead, + splitBuilder.build(), + new Constraints(constraintsMap), + 100_000_000_000L, //100GB don't expect this to spill + 100_000_000_000L + ); + + RecordResponse rawResponse = handler.doReadRecords(allocator, request); + + assertTrue(rawResponse instanceof ReadRecordsResponse); + + ReadRecordsResponse response = (ReadRecordsResponse) rawResponse; + logger.info("doReadRecordsNoSpill: rows[{}]", response.getRecordCount()); + + assertTrue(response.getRecords().getRowCount() == 1); + logger.info("doReadRecordsNoSpill: {}", BlockUtils.rowToString(response.getRecords(), 0)); + + logger.info("doReadRecordsNoSpill: exit"); + } + + @Test + public void doReadRecordsSpill() + throws Exception + { + logger.info("doReadRecordsSpill: enter"); + + String schema = "schema1"; + String table = "table1"; + + List results = TestUtils.makeResults(10_000); + ResultScanner mockScanner = mock(ResultScanner.class); + when(mockTable.getScanner(any(Scan.class))).thenReturn(mockScanner); + when(mockScanner.iterator()).thenReturn(results.iterator()); + + Map constraintsMap = new HashMap<>(); + constraintsMap.put("family1:col3", SortedRangeSet.copyOf(Types.MinorType.BIGINT.getType(), + ImmutableList.of(Range.greaterThan(allocator, Types.MinorType.BIGINT.getType(), 0L)), true)); + + S3SpillLocation splitLoc = S3SpillLocation.newBuilder() + .withBucket(UUID.randomUUID().toString()) + .withSplitId(UUID.randomUUID().toString()) + .withQueryId(UUID.randomUUID().toString()) + .withIsDirectory(true) + .build(); + + Split.Builder splitBuilder = Split.newBuilder(splitLoc, keyFactory.create()) + .add(HBASE_CONN_STR, "fake_con_str") + .add(START_KEY_FIELD, "fake_start_key") + .add(END_KEY_FIELD, "fake_end_key") + .add(REGION_ID_FIELD, "fake_region_id") + .add(REGION_NAME_FIELD, "fake_region_name"); + + ReadRecordsRequest request = new ReadRecordsRequest(identity, + catalog, + "queryId-" + System.currentTimeMillis(), + new TableName(schema, table), + schemaForRead, + splitBuilder.build(), + new Constraints(constraintsMap), + 1_500_000L, //~1.5MB so we should see some spill + 0L + ); + RecordResponse rawResponse = handler.doReadRecords(allocator, request); + + assertTrue(rawResponse instanceof RemoteReadRecordsResponse); + + try (RemoteReadRecordsResponse response = (RemoteReadRecordsResponse) rawResponse) { + logger.info("doReadRecordsSpill: remoteBlocks[{}]", response.getRemoteBlocks().size()); + + assertTrue(response.getNumberBlocks() > 1); + + int blockNum = 0; + for (SpillLocation next : response.getRemoteBlocks()) { + S3SpillLocation spillLocation = (S3SpillLocation) next; + try (Block block = spillReader.read(spillLocation, response.getEncryptionKey(), response.getSchema())) { + + logger.info("doReadRecordsSpill: blockNum[{}] and recordCount[{}]", blockNum++, block.getRowCount()); + // assertTrue(++blockNum < response.getRemoteBlocks().size() && block.getRowCount() > 10_000); + + logger.info("doReadRecordsSpill: {}", BlockUtils.rowToString(block, 0)); + assertNotNull(BlockUtils.rowToString(block, 0)); + } + } + } + + logger.info("doReadRecordsSpill: exit"); + } + + private class ByteHolder + { + private byte[] bytes; + + public void setBytes(byte[] bytes) + { + this.bytes = bytes; + } + + public byte[] getBytes() + { + return bytes; + } + } +} diff --git a/athena-hbase/src/test/java/com/amazonaws/athena/connectors/hbase/HbaseSchemaUtilsTest.java b/athena-hbase/src/test/java/com/amazonaws/athena/connectors/hbase/HbaseSchemaUtilsTest.java new file mode 100644 index 0000000000..b1d8ede80f --- /dev/null +++ b/athena-hbase/src/test/java/com/amazonaws/athena/connectors/hbase/HbaseSchemaUtilsTest.java @@ -0,0 +1,175 @@ +/*- + * #%L + * athena-hbase + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.hbase; + +import com.amazonaws.athena.connector.lambda.domain.TableName; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Schema; +import org.apache.hadoop.hbase.KeyValue; +import org.apache.hadoop.hbase.client.Connection; +import org.apache.hadoop.hbase.client.Result; +import org.apache.hadoop.hbase.client.ResultScanner; +import org.apache.hadoop.hbase.client.Scan; +import org.apache.hadoop.hbase.client.Table; +import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; + +import static com.amazonaws.athena.connectors.hbase.HbaseSchemaUtils.coerceType; +import static com.amazonaws.athena.connectors.hbase.HbaseSchemaUtils.toBytes; +import static com.amazonaws.athena.connectors.hbase.TestUtils.makeResult; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class HbaseSchemaUtilsTest +{ + @Test + public void inferSchema() + throws IOException + { + int numToScan = 4; + TableName tableName = new TableName("schema", "table"); + List results = TestUtils.makeResults(); + + Connection mockConnection = mock(Connection.class); + Table mockTable = mock(Table.class); + ResultScanner mockScanner = mock(ResultScanner.class); + + when(mockConnection.getTable(any(org.apache.hadoop.hbase.TableName.class))).thenAnswer((InvocationOnMock invocation) -> { + org.apache.hadoop.hbase.TableName table = invocation.getArgumentAt(0, org.apache.hadoop.hbase.TableName.class); + assertEquals(tableName.getSchemaName() + ":" + tableName.getTableName(), table.getNameAsString()); + return mockTable; + }); + + when(mockTable.getScanner(any(Scan.class))).thenAnswer((InvocationOnMock invocation) -> { + Scan scan = invocation.getArgumentAt(0, Scan.class); + assertEquals(numToScan, scan.getMaxResultSize()); + return mockScanner; + }); + when(mockScanner.iterator()).thenReturn(results.iterator()); + + Schema schema = HbaseSchemaUtils.inferSchema(mockConnection, tableName, numToScan); + + Map actualFields = new HashMap<>(); + schema.getFields().stream().forEach(next -> actualFields.put(next.getName(), Types.getMinorTypeForArrowType(next.getType()))); + + Map expectedFields = new HashMap<>(); + TestUtils.makeSchema().build().getFields().stream() + .forEach(next -> expectedFields.put(next.getName(), Types.getMinorTypeForArrowType(next.getType()))); + + for (Map.Entry nextExpected : expectedFields.entrySet()) { + assertNotNull(actualFields.get(nextExpected.getKey())); + assertEquals(nextExpected.getKey(), nextExpected.getValue(), actualFields.get(nextExpected.getKey())); + } + assertEquals(expectedFields.size(), actualFields.size()); + + verify(mockConnection, times(1)).getTable(any()); + verify(mockTable, times(1)).getScanner(any(Scan.class)); + verify(mockScanner, times(1)).iterator(); + } + + @Test + public void getQualifiedTableName() + { + String table = "table"; + String schema = "schema"; + String expected = "schema:table"; + String actual = HbaseSchemaUtils.getQualifiedTableName(new TableName(schema, table)); + assertEquals(expected, actual); + } + + @Test + public void getQualifiedTable() + { + String table = "table"; + String schema = "schema"; + org.apache.hadoop.hbase.TableName expected = org.apache.hadoop.hbase.TableName.valueOf(schema + ":" + table); + org.apache.hadoop.hbase.TableName actual = HbaseSchemaUtils.getQualifiedTable(new TableName(schema, table)); + assertEquals(expected, actual); + } + + @Test + public void inferType() + { + assertEquals(Types.MinorType.BIGINT, HbaseSchemaUtils.inferType("1".getBytes())); + assertEquals(Types.MinorType.BIGINT, HbaseSchemaUtils.inferType("1000".getBytes())); + assertEquals(Types.MinorType.BIGINT, HbaseSchemaUtils.inferType("-1".getBytes())); + assertEquals(Types.MinorType.FLOAT8, HbaseSchemaUtils.inferType("1.0".getBytes())); + assertEquals(Types.MinorType.FLOAT8, HbaseSchemaUtils.inferType(".01".getBytes())); + assertEquals(Types.MinorType.FLOAT8, HbaseSchemaUtils.inferType("-.01".getBytes())); + assertEquals(Types.MinorType.VARCHAR, HbaseSchemaUtils.inferType("BDFKD".getBytes())); + assertEquals(Types.MinorType.VARCHAR, HbaseSchemaUtils.inferType("".getBytes())); + } + + @Test + public void coerceTypeTest() + { + boolean isNative = false; + assertEquals("asf", coerceType(isNative, Types.MinorType.VARCHAR.getType(), "asf".getBytes())); + assertEquals("2.0", coerceType(isNative, Types.MinorType.VARCHAR.getType(), "2.0".getBytes())); + assertEquals(1, coerceType(isNative, Types.MinorType.INT.getType(), "1".getBytes())); + assertEquals(-1, coerceType(isNative, Types.MinorType.INT.getType(), "-1".getBytes())); + assertEquals(1L, coerceType(isNative, Types.MinorType.BIGINT.getType(), "1".getBytes())); + assertEquals(-1L, coerceType(isNative, Types.MinorType.BIGINT.getType(), "-1".getBytes())); + assertEquals(1.1F, coerceType(isNative, Types.MinorType.FLOAT4.getType(), "1.1".getBytes())); + assertEquals(-1.1F, coerceType(isNative, Types.MinorType.FLOAT4.getType(), "-1.1".getBytes())); + assertEquals(1.1D, coerceType(isNative, Types.MinorType.FLOAT8.getType(), "1.1".getBytes())); + assertEquals(-1.1D, coerceType(isNative, Types.MinorType.FLOAT8.getType(), "-1.1".getBytes())); + assertArrayEquals("-1.1".getBytes(), (byte[]) coerceType(isNative, Types.MinorType.VARBINARY.getType(), "-1.1".getBytes())); + } + + @Test + public void coerceTypeNativeTest() + { + boolean isNative = true; + assertEquals("asf", coerceType(isNative, Types.MinorType.VARCHAR.getType(), "asf".getBytes())); + assertEquals("2.0", coerceType(isNative, Types.MinorType.VARCHAR.getType(), "2.0".getBytes())); + assertEquals(1, coerceType(isNative, Types.MinorType.INT.getType(), toBytes(isNative, 1))); + assertEquals(-1, coerceType(isNative, Types.MinorType.INT.getType(), toBytes(isNative, -1))); + assertEquals(1L, coerceType(isNative, Types.MinorType.BIGINT.getType(), toBytes(isNative, 1L))); + assertEquals(-1L, coerceType(isNative, Types.MinorType.BIGINT.getType(), toBytes(isNative, -1L))); + assertEquals(1.1F, coerceType(isNative, Types.MinorType.FLOAT4.getType(), toBytes(isNative, 1.1F))); + assertEquals(-1.1F, coerceType(isNative, Types.MinorType.FLOAT4.getType(), toBytes(isNative, -1.1F))); + assertEquals(1.1D, coerceType(isNative, Types.MinorType.FLOAT8.getType(), toBytes(isNative, 1.1D))); + assertEquals(-1.1D, coerceType(isNative, Types.MinorType.FLOAT8.getType(), toBytes(isNative, -1.1D))); + assertArrayEquals("-1.1".getBytes(), (byte[]) coerceType(isNative, Types.MinorType.VARBINARY.getType(), "-1.1".getBytes())); + } + + @Test + public void extractColumnParts() + { + String[] parts = HbaseSchemaUtils.extractColumnParts("family:column"); + assertEquals("family", parts[0]); + assertEquals("column", parts[1]); + } +} diff --git a/athena-hbase/src/test/java/com/amazonaws/athena/connectors/hbase/TestUtils.java b/athena-hbase/src/test/java/com/amazonaws/athena/connectors/hbase/TestUtils.java new file mode 100644 index 0000000000..98ff8147c1 --- /dev/null +++ b/athena-hbase/src/test/java/com/amazonaws/athena/connectors/hbase/TestUtils.java @@ -0,0 +1,162 @@ +/*- + * #%L + * athena-hbase + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.hbase; + +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Schema; +import org.apache.hadoop.hbase.HRegionInfo; +import org.apache.hadoop.hbase.KeyValue; +import org.apache.hadoop.hbase.client.Result; +import org.mockito.invocation.InvocationOnMock; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class TestUtils +{ + private static final Logger logger = LoggerFactory.getLogger(TestUtils.class); + + private TestUtils() {} + + public static SchemaBuilder makeSchema() + { + return SchemaBuilder.newBuilder() + .addStringField("family1:col1") + .addStringField("family1:col2") + .addBigIntField("family1:col3") + .addStringField("family1:col10") + .addFloat8Field("family1:col20") + .addBigIntField("family1:col30") + .addStringField("family2:col1") + .addFloat8Field("family2:col2") + .addBigIntField("family2:col3") + .addStringField("family2:col10") + .addFloat8Field("family2:col20") + .addBigIntField("family2:col30") + .addBigIntField("family3:col1"); + } + + public static List makeResults() + { + List results = new ArrayList<>(); + //Initial row with two column familyies and a mix of columns and types + results.add(makeResult("family1", "col1", "varchar", + "family1", "col2", "1.0", + "family1", "col3", "1", + "family2", "col1", "varchar", + "family2", "col2", "1.0", + "family2", "col3", "1" + )); + + //Add a row which has only new columns to ensure we get a union + results.add(makeResult("family1", "col10", "varchar", + "family1", "col20", "1.0", + "family1", "col30", "1", + "family2", "col10", "varchar", + "family2", "col20", "1.0", + "family2", "col30", "1", + "family3", "col1", "1" + )); + + //Add a 2nd occurance of col2 in family1 but with a conflicting type, it should result in a final type of varchar + results.add(makeResult("family1", "col2", "1")); + + return results; + } + + public static List makeResults(int numResults) + { + List results = new ArrayList<>(); + + for (int i = 0; i < numResults; i++) { + //Initial row with two column familyies and a mix of columns and types + results.add(makeResult("family1", "col1", "varchar" + i, + "family1", "col2", String.valueOf(i * 1.1), + "family1", "col3", String.valueOf(i), + "family2", "col1", "varchar" + i, + "family2", "col2", String.valueOf(i * 1.1), + "family2", "col3", String.valueOf(i) + )); + + //Add a row which has only new columns to ensure we get a union + results.add(makeResult("family1", "col10", "varchar" + i, + "family1", "col20", String.valueOf(i * 1.1), + "family1", "col30", String.valueOf(i), + "family2", "col10", "varchar" + i, + "family2", "col20", String.valueOf(i * 1.1), + "family2", "col30", String.valueOf(i), + "family3", "col1", String.valueOf(i) + )); + + //Add a 2nd occurance of col2 in family1 but with a conflicting type, it should result in a final type of varchar + results.add(makeResult("family1", "col2", String.valueOf(i))); + } + + return results; + } + + public static Result makeResult(String... values) + { + if (values.length % 3 != 0) { + throw new RuntimeException("Method requires values in multiples of 3 -> family, qualifier, value"); + } + + List result = new ArrayList<>(); + Map valueMap = new HashMap<>(); + for (int i = 0; i < values.length; i += 3) { + KeyValue mockKeyValue = mock(KeyValue.class); + when(mockKeyValue.getFamily()).thenReturn(values[i].getBytes()); + when(mockKeyValue.getQualifier()).thenReturn(values[i + 1].getBytes()); + when(mockKeyValue.getValue()).thenReturn(values[i + 2].getBytes()); + valueMap.put(values[i] + ":" + values[i + 1], values[i + 2]); + result.add(mockKeyValue); + } + + Result mockResult = mock(Result.class); + when(mockResult.list()).thenReturn(result); + when(mockResult.getValue(any(byte[].class), any(byte[].class))) + .thenAnswer((InvocationOnMock invocation) -> { + String family = new String(invocation.getArgumentAt(0, byte[].class)); + String column = new String(invocation.getArgumentAt(1, byte[].class)); + String key = family + ":" + column; + if (!valueMap.containsKey(key)) { + return null; + } + else { + return valueMap.get(key).getBytes(); + } + }); + return mockResult; + } + + public static HRegionInfo makeRegion(int id, String schema, String table) + { + return new HRegionInfo(id, org.apache.hadoop.hbase.TableName.valueOf(schema, table), 1); + } +} diff --git a/athena-jdbc/LICENSE.txt b/athena-jdbc/LICENSE.txt new file mode 100644 index 0000000000..418de4c108 --- /dev/null +++ b/athena-jdbc/LICENSE.txt @@ -0,0 +1,174 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. \ No newline at end of file diff --git a/athena-jdbc/README.md b/athena-jdbc/README.md new file mode 100644 index 0000000000..a960eb3d1f --- /dev/null +++ b/athena-jdbc/README.md @@ -0,0 +1,176 @@ +# Amazon Athena Lambda Jdbc Connector + +This connector enables Amazon Athena to access your SQL database or RDS instance(s) using JDBC driver. + +Following databases are supported: + +1. MySql +2. PostGreSql +3. Redshift + +See `com.amazonaws.connectors.athena.jdbc.connectio.JdbcConnectionFactory.DatabaseEngine` for latest database types supported. + +# Terms + +* **Database Instance:** Any instance of a database deployed on premises, EC2 or using RDS. +* **Database type:** Could be one of mysql, postgres, redshift. +* **Handler:** A Lambda handler accessing your database instance(s). Could be metadata or a record handler. +* **Metadata Handler:** A Lambda handler that retrieves metadata from your database instance(s). +* **Record Handler:** A Lambda handler that retrieves data records from your database instance(s). +* **Property/Parameter:** A database property used by handlers to extract database information for connection. These are set as Lambda environment variables. +* **Connection String:** Used to establish connection to a database instance. +* **Catalog:** Athena Catalog. This is not a Glue Catalog. Must be used to prefix `connection_string` property. + +# Usage + +## Parameters + +Jdbc Connector supports several configuration parameters using Lambda environment variables. Each parameter should be prefixed with a database instance name (any unique string) except spill s3 bucket and prefix. + + +## Connection String: + +Connection string is used to connect to a database instance. + +We support following format: + +`${db_type}://` + +``` +db_type One of following, mysql, postgres, redshift. +jdbc_connection_string Connection string for a database type. For example, MySql connection String: jdbc:mysql://host1:33060/database +``` + + +## Multiplexing handler parameters + +Multiplexer provides a way to connect to multiple database instances of any type using a single Lambda function. Requests are routed depending on catalog name. Use following classes in Lambda for using multiplexer. + +|DB Type|MetadataHandler|RecordHandler| +|--- |--- |--- | +|All supported database types|com.amazonaws.connectors.athena.jdbc.MultiplexingJdbcMetadataHandler|com.amazonaws.connectors.athena.jdbc.MultiplexingJdbcRecordHandler| + +**Parameters:** + +``` +_connection_string Database instance connection string. One of two types specified above. Required. +default Default connection string. Required. This will be used when a catalog is not recognized. +``` + +Example properties for a Mux Lambda function that supports three database instances, mysql1, mysql2 and postgres1: + +|Property|Value| +|---|---| +|mysql_catalog1_connection_string |mysql://jdbc:mysql://mysql1.host:3306/default?${Test/RDS/PostGres1}| +| | | +| | | +|mysql_catalog2_connection_string |mysql://jdbc:mysql://mysql2.host:3333/default?user=sample2&password=sample2| +| | | +| | | +|postgres_catalog3_connection_string |postgres://jdbc:postgresql://postgres1.host:5432/default?${Test/RDS/PostGres1}| + +JDBC Connector supports substitution of any string enclosed like ${SecretName} with username and password retrieved from AWS Secrets Manager. Example, `mysql://jdbc:mysql://mysql1.host:3306/default?...&${Test/RDS/PostGres1}&...` will be replaced to `mysql://jdbc:mysql://mysql1.host:3306/default?...&user=sample2&password=sample2&...` and secret name `Test/RDS/PostGres1` will be used to retrieve secrets. + +## Database specific handler parameters + +Database specific metadata and record handlers can also be used to connect to a database instance. These are currently capable of connecting to a single database instance. + +|DB Type|MetadataHandler|RecordHandler| +|---|---|---| +|MySql|com.amazonaws.connectors.athena.jdbc.mysql.MySqlMetadataHandler|com.amazonaws.connectors.athena.jdbc.mysql.MySqlRecordHandler| +|PostGreSql|com.amazonaws.connectors.athena.jdbc.postgresql.PostGreSqlMetadataHandler|com.amazonaws.connectors.athena.jdbc.postgresql.PostGreSqlRecordHandler| +|Redshift|com.amazonaws.connectors.athena.jdbc.postgresql.PostGreSqlMetadataHandler|com.amazonaws.connectors.athena.jdbc.postgresql.PostGreSqlRecordHandler| + +**Parameters:** + +``` +default Default connection string. Required. This will be used when a catalog is not recognized. +``` + +These handlers support one database instance. Must provide `default` parameter, everything else is ignored. + +**Example property for a single MySql instance supported by a Lambda function:** + +| Property | Value | +| --- | --- | +| catalog1_connection_string | mysql://mysql1.host:3306/default?secret=Test/RDS/MySql1 | + +## Common parameters + +### Spill parameters: + +All database instances accessed using Lambda spill to the same location + +``` +spill_bucket Bucket name for spill. Required. +spill_prefix Spill bucket key prefix. Required. +``` + +What is spilled? + +# Data types support + +|Jdbc|Arrow| +| ---|---| +|Boolean|Bit| +|Integer|Tiny| +|Short|Smallint| +|Integer|Int| +|Long|Bigint| +|float|Float4| +|Double|Float8| +|Date|DateDay| +|Timestamp|DateMilli| +|String|Varchar| +|Bytes|Varbinary| +|BigDecimal|Decimal| + +See respective database documentation for conversion between JDBC and database types. + +# Secrets + +We support two ways to input database user name and password: + +1. **AWS Secrets Manager:** The name of the secret in AWS Secrets Manager can be embedded in JDBC connection string, which is used to replace with `username` and `password` in secret value. Support is tightly integrated for AWS RDS database instances. When using AWS RDS, we highly recommend using AWS Secrets Manager, including credential rotation. If your database is not using AWS RDS, store credentials as JSON in the following format `{“username”: “${username}”, “password”: “${password}”}.`. +2. **Basic Auth:** Username and password can be specified in the JDBC connection string. + +# Partitions and Splits +### MySql +A partition is represented by a single partition column of type varchar. We leverage partitions defined on a MySql table, and this column contains partition names. For a table that does not have partition names, * is returned which single partition. A partition is equivalent to a split. + +|Name|Type|Description +|---|---|---| +|partition_name|Varchar|Named partition in MySql. E.g. p0| + + +### PostGreSql & Redshift +A partition is represented by two partition columns of type varchar. We leverage partitions as child tables defined on a PostGres table, and these columns contain child schema and child table information. For a table that does not have partition names, * is returned which single partition. A partition is equivalent to a split. + +|Name|Type|Description +|---|---|---| +|partition_schema|Varchar|Child table schema name| +|partition_name|Varchar|Child table name| + +In case of Redshift partition_schema and partition_name will always be "*". + +### Deploying The Connector + +To use the Amazon Athena HBase Connector in your queries, navigate to AWS Serverless Application Repository and deploy a pre-built version of this connector. Alternatively, you can build and deploy this connector from source follow the below steps or use the more detailed tutorial in the athena-example module: + +1. From the athena-federation-sdk dir, run `mvn clean install` if you haven't already. +2. From the athena-jdbc dir, run `mvn clean install`. +3. From the athena-jdbc dir, run `../tools/publish.sh S3_BUCKET_NAME athena-jdbc` to publish the connector to your private AWS Serverless Application Repository. The S3_BUCKET in the command is where a copy of the connector's code will be stored for Serverless Application Repository to retrieve it. This will allow users with permission to do so, the ability to deploy instances of the connector via 1-Click form. Then navigate to [Serverless Application Repository](https://aws.amazon.com/serverless/serverlessrepo) + +# JDBC Driver Versions +Current supported versions of JDBC Driver: + +|Database Type|Version| +|---|---| +|MySql|8.0.17| +|PostGreSql|42.2.8| +|Redshift|1.2.34.1058| + +# Limitations +* Write DDL operations are not supported. Athena can only do read operations currently. Athena assumes tables and relevant entities already exist in database instance. +* In Mux setup, spill bucket and prefix is shared across all database instances. +* Any relevant Lambda Limits. See Lambda documentation. diff --git a/athena-jdbc/athena-jdbc.yaml b/athena-jdbc/athena-jdbc.yaml new file mode 100644 index 0000000000..1d8f484456 --- /dev/null +++ b/athena-jdbc/athena-jdbc.yaml @@ -0,0 +1,102 @@ +Transform: 'AWS::Serverless-2016-10-31' +Metadata: + 'AWS::ServerlessRepo::Application': + Name: AthenaJdbcConnector + Description: 'This connector enables Amazon Athena to communicate with your Database instance(s) using JDBC driver.' + Author: 'Amazon Athena' + SpdxLicenseId: Apache-2.0 + LicenseUrl: LICENSE.txt + ReadmeUrl: README.md + Labels: + - athena-federation + HomePageUrl: 'https://github.com/awslabs/aws-athena-query-federation' + SemanticVersion: 1.0.0 + SourceCodeUrl: 'https://github.com/awslabs/aws-athena-query-federation' +Parameters: + LambdaFunctionName: + Description: 'The name you will give to the Function accessing Database instance. This is also the name you can use to query your database from athena using the registration-less catalog of “lambda:”.' + Type: String + DefaultConnectionString: + Description: 'The default connection string is used when catalog is "lambda:${LambdaFunctionName}". Catalog specific Connection Strings can be added later. Format: ${DatabaseType}://${NativeJdbcConnectionString}.' + Type: String + SecretNamePrefix: + Description: 'Used to create resource-based authorization policy for "secretsmanager:GetSecretValue" action. E.g. All Athena JDBC Federation secret names can be prefixed with "AthenaJdbcFederation" and authorization policy will allow "arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:AthenaJdbcFederation*". Parameter value in this case should be "AthenaJdbcFederation". If you do not have a prefix, you can manually update the IAM policy to add allow any secret names.' + Type: String + SpillBucket: + Description: 'The bucket where this function can spill data.' + Type: String + Default: athena-federation-spill + SpillPrefix: + Description: 'The bucket prefix where this function can spill large responses.' + Type: String + Default: athena-spill/jdbc + LambdaTimeout: + Description: 'Maximum Lambda invocation runtime in seconds. (min 1 - 900 max)' + Default: 900 + Type: Number + LambdaMemory: + Description: 'Lambda memory in MB (min 128 - 3008 max).' + Default: 3008 + Type: Number + DisableSpillEncryption: + Description: 'If set to ''false'' data spilled to S3 is encrypted with AES GCM' + Default: 'false' + Type: String + SecurityGroupIds: + Description: 'One or more SecurityGroup IDs corresponding to the SecurityGroup that should be applied to the Lambda function. (e.g. sg1,sg2,sg3)' + Type: 'List' + SubnetIds: + Description: 'One or more Subnet IDs corresponding to the Subnet that the Lambda function can use to access you data source. (e.g. subnet1,subnet2)' + Type: 'List' +Resources: + JdbcConnectorConfig: + Type: 'AWS::Serverless::Function' + Properties: + Environment: + Variables: + disable_spill_encryption: !Ref DisableSpillEncryption + spill_bucket: !Ref SpillBucket + spill_prefix: !Ref SpillPrefix + default: !Ref DefaultConnectionString + FunctionName: !Ref LambdaFunctionName + Handler: "com.amazonaws.connectors.athena.jdbc.MultiplexingJdbcCompositeHandler" + CodeUri: "./target/athena-jdbc-1.0.jar" + Description: "Enables Amazon Athena to communicate with Databases using JDBC" + Runtime: java8 + Timeout: !Ref LambdaTimeout + MemorySize: !Ref LambdaMemory + Policies: + - Statement: + - Action: + - secretsmanager:GetSecretValue + Effect: Allow + Resource: !Sub 'arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:${SecretNamePrefix}*' + Version: '2012-10-17' + - Statement: + - Action: + - logs:CreateLogGroup + Effect: Allow + Resource: !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:*' + Version: '2012-10-17' + - Statement: + - Action: + - logs:CreateLogStream + - logs:PutLogEvents + Effect: Allow + Resource: !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${LambdaFunctionName}:*' + Version: '2012-10-17' + - Statement: + - Action: + - athena:GetQueryExecution + Effect: Allow + Resource: '*' + Version: '2012-10-17' + #S3CrudPolicy allows our connector to spill large responses to S3. You can optionally replace this pre-made policy + #with one that is more restrictive and can only 'put' but not read,delete, or overwrite files. + - S3CrudPolicy: + BucketName: !Ref SpillBucket + #VPCAccessPolicy allows our connector to run in a VPC so that it can access your data source. + - VPCAccessPolicy: {} + VpcConfig: + SecurityGroupIds: !Ref SecurityGroupIds + SubnetIds: !Ref SubnetIds \ No newline at end of file diff --git a/athena-jdbc/pom.xml b/athena-jdbc/pom.xml new file mode 100644 index 0000000000..1c06e9991c --- /dev/null +++ b/athena-jdbc/pom.xml @@ -0,0 +1,92 @@ + + + + aws-athena-query-federation + com.amazonaws + 1.0 + + 4.0.0 + + athena-jdbc + + + + com.amazonaws + aws-athena-federation-sdk + ${aws-athena-federation-sdk.version} + + + org.apache.commons + commons-text + 1.8 + + + mysql + mysql-connector-java + 8.0.17 + + + org.antlr + stringtemplate + 4.0.2 + + + com.amazonaws + aws-java-sdk-secretsmanager + ${aws-sdk.version} + + + org.apache.arrow + arrow-jdbc + 0.15.0 + + + org.postgresql + postgresql + 42.2.8 + + + com.amazon.redshift + redshift-jdbc42 + 1.2.34.1058 + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.1 + + false + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + package + + shade + + + + + + + + + + redshift + https://s3.amazonaws.com/redshift-maven-repository/release + + + diff --git a/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/MultiplexingJdbcCompositeHandler.java b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/MultiplexingJdbcCompositeHandler.java new file mode 100644 index 0000000000..752bd14122 --- /dev/null +++ b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/MultiplexingJdbcCompositeHandler.java @@ -0,0 +1,35 @@ +/*- + * #%L + * athena-jdbc + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.jdbc; + +import com.amazonaws.athena.connector.lambda.handlers.CompositeHandler; + +/** + * Boilerplate composite handler that allows us to use a single Lambda function for both + * Metadata and Data. In this case we just compose {@link MultiplexingJdbcMetadataHandler} and {@link MultiplexingJdbcRecordHandler}. + */ +public class MultiplexingJdbcCompositeHandler + extends CompositeHandler +{ + public MultiplexingJdbcCompositeHandler() + { + super(new MultiplexingJdbcMetadataHandler(), new MultiplexingJdbcRecordHandler()); + } +} diff --git a/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/MultiplexingJdbcMetadataHandler.java b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/MultiplexingJdbcMetadataHandler.java new file mode 100644 index 0000000000..ae75e5f6e1 --- /dev/null +++ b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/MultiplexingJdbcMetadataHandler.java @@ -0,0 +1,137 @@ +/*- + * #%L + * athena-jdbc + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.jdbc; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockWriter; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesResponse; +import com.amazonaws.connectors.athena.jdbc.connection.DatabaseConnectionConfig; +import com.amazonaws.connectors.athena.jdbc.connection.JdbcConnectionFactory; +import com.amazonaws.connectors.athena.jdbc.manager.JDBCUtil; +import com.amazonaws.connectors.athena.jdbc.manager.JdbcMetadataHandler; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.google.common.annotations.VisibleForTesting; +import org.apache.arrow.vector.types.pojo.Schema; +import org.apache.commons.lang3.Validate; + +import java.util.Map; + +/** + * Metadata handler multiplexer that supports multiple engines e.g. MySQL, Oracle, etc. in same Lambda. + */ +public class MultiplexingJdbcMetadataHandler + extends JdbcMetadataHandler +{ + private static final int MAX_CATALOGS_TO_MULTIPLEX = 100; + private final Map metadataHandlerMap; + + /** + * @param metadataHandlerMap catalog -> JdbcMetadataHandler + */ + @VisibleForTesting + MultiplexingJdbcMetadataHandler(final AWSSecretsManager secretsManager, final AmazonAthena athena, final JdbcConnectionFactory jdbcConnectionFactory, + final Map metadataHandlerMap, final DatabaseConnectionConfig databaseConnectionConfig) + { + super(databaseConnectionConfig, secretsManager, athena, jdbcConnectionFactory); + this.metadataHandlerMap = Validate.notEmpty(metadataHandlerMap, "metadataHandlerMap must not be empty"); + + if (this.metadataHandlerMap.size() > MAX_CATALOGS_TO_MULTIPLEX) { + throw new RuntimeException("Max 100 catalogs supported in multiplexer."); + } + } + + /** + * Initializes mux routing map. Creates a reverse index of Athena catalogs supported by a database instance. Max 100 catalogs supported currently. + */ + public MultiplexingJdbcMetadataHandler() + { + this.metadataHandlerMap = Validate.notEmpty(JDBCUtil.createJdbcMetadataHandlerMap(System.getenv()), "Could not find any delegatee."); + } + + private void validateMultiplexer(final String catalogName) + { + if (this.metadataHandlerMap.get(catalogName) == null) { + throw new RuntimeException("Catalog not supported in multiplexer " + catalogName); + } + } + + @Override + public Schema getPartitionSchema(final String catalogName) + { + validateMultiplexer(catalogName); + return this.metadataHandlerMap.get(catalogName).getPartitionSchema(catalogName); + } + + @Override + public ListSchemasResponse doListSchemaNames(BlockAllocator blockAllocator, ListSchemasRequest listSchemasRequest) + { + validateMultiplexer(listSchemasRequest.getCatalogName()); + return this.metadataHandlerMap.get(listSchemasRequest.getCatalogName()).doListSchemaNames(blockAllocator, listSchemasRequest); + } + + @Override + public ListTablesResponse doListTables(BlockAllocator blockAllocator, ListTablesRequest listTablesRequest) + { + validateMultiplexer(listTablesRequest.getCatalogName()); + return this.metadataHandlerMap.get(listTablesRequest.getCatalogName()).doListTables(blockAllocator, listTablesRequest); + } + + @Override + public GetTableResponse doGetTable(BlockAllocator blockAllocator, GetTableRequest getTableRequest) + { + validateMultiplexer(getTableRequest.getCatalogName()); + return this.metadataHandlerMap.get(getTableRequest.getCatalogName()).doGetTable(blockAllocator, getTableRequest); + } + + @Override + public void getPartitions(final BlockWriter blockWriter, final GetTableLayoutRequest getTableLayoutRequest, QueryStatusChecker queryStatusChecker) + throws Exception + { + validateMultiplexer(getTableLayoutRequest.getCatalogName()); + this.metadataHandlerMap.get(getTableLayoutRequest.getCatalogName()).getPartitions(blockWriter, getTableLayoutRequest, queryStatusChecker); + } + + @Override + public GetTableLayoutResponse doGetTableLayout(BlockAllocator blockAllocator, GetTableLayoutRequest getTableLayoutRequest) + throws Exception + { + validateMultiplexer(getTableLayoutRequest.getCatalogName()); + return this.metadataHandlerMap.get(getTableLayoutRequest.getCatalogName()).doGetTableLayout(blockAllocator, getTableLayoutRequest); + } + + @Override + public GetSplitsResponse doGetSplits( + final BlockAllocator blockAllocator, final GetSplitsRequest getSplitsRequest) + { + validateMultiplexer(getSplitsRequest.getCatalogName()); + return this.metadataHandlerMap.get(getSplitsRequest.getCatalogName()).doGetSplits(blockAllocator, getSplitsRequest); + } +} diff --git a/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/MultiplexingJdbcRecordHandler.java b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/MultiplexingJdbcRecordHandler.java new file mode 100644 index 0000000000..fd75bf0598 --- /dev/null +++ b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/MultiplexingJdbcRecordHandler.java @@ -0,0 +1,92 @@ +/*- + * #%L + * athena-jdbc + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.jdbc; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.BlockSpiller; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.connectors.athena.jdbc.connection.DatabaseConnectionConfig; +import com.amazonaws.connectors.athena.jdbc.connection.JdbcConnectionFactory; +import com.amazonaws.connectors.athena.jdbc.manager.JDBCUtil; +import com.amazonaws.connectors.athena.jdbc.manager.JdbcRecordHandler; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.google.common.annotations.VisibleForTesting; +import org.apache.arrow.vector.types.pojo.Schema; +import org.apache.commons.lang3.Validate; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.Map; + +/** + * Record handler multiplexer that supports multiple engines e.g. MySQL, Oracle, etc. in same Lambda. + */ +public class MultiplexingJdbcRecordHandler + extends JdbcRecordHandler +{ + private static final int MAX_CATALOGS_TO_MULTIPLEX = 100; + private final Map recordHandlerMap; + + public MultiplexingJdbcRecordHandler() + { + this.recordHandlerMap = Validate.notEmpty(JDBCUtil.createJdbcRecordHandlerMap(System.getenv()), "Could not find any delegatee."); + } + + @VisibleForTesting + MultiplexingJdbcRecordHandler(final AmazonS3 amazonS3, final AWSSecretsManager secretsManager, final AmazonAthena athena, final JdbcConnectionFactory jdbcConnectionFactory, + final DatabaseConnectionConfig databaseConnectionConfig, final Map recordHandlerMap) + { + super(amazonS3, secretsManager, athena, databaseConnectionConfig, jdbcConnectionFactory); + this.recordHandlerMap = Validate.notEmpty(recordHandlerMap, "recordHandlerMap must not be empty"); + + if (this.recordHandlerMap.size() > MAX_CATALOGS_TO_MULTIPLEX) { + throw new RuntimeException("Max 100 catalogs supported in multiplexer."); + } + } + + private void validateMultiplexer(final String catalogName) + { + if (this.recordHandlerMap.get(catalogName) == null) { + throw new RuntimeException("Catalog not supported in multiplexer " + catalogName); + } + } + + @Override + public void readWithConstraint( + final BlockSpiller blockSpiller, + final ReadRecordsRequest readRecordsRequest, QueryStatusChecker queryStatusChecker) + { + validateMultiplexer(readRecordsRequest.getCatalogName()); + this.recordHandlerMap.get(readRecordsRequest.getCatalogName()).readWithConstraint(blockSpiller, readRecordsRequest, queryStatusChecker); + } + + @Override + public PreparedStatement buildSplitSql(Connection jdbcConnection, String catalogName, TableName tableName, Schema schema, Constraints constraints, Split split) + throws SQLException + { + return this.recordHandlerMap.get(catalogName).buildSplitSql(jdbcConnection, catalogName, tableName, schema, constraints, split); + } +} diff --git a/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/connection/DatabaseConnectionConfig.java b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/connection/DatabaseConnectionConfig.java new file mode 100644 index 0000000000..c2d3c40cba --- /dev/null +++ b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/connection/DatabaseConnectionConfig.java @@ -0,0 +1,100 @@ +/*- + * #%L + * athena-jdbc + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.jdbc.connection; + +import org.apache.commons.lang3.Validate; + +import java.util.Objects; + +public class DatabaseConnectionConfig +{ + private String catalog; + private final JdbcConnectionFactory.DatabaseEngine type; + private final String jdbcConnectionString; + private String secret; + + public DatabaseConnectionConfig(final String catalog, final JdbcConnectionFactory.DatabaseEngine type, final String jdbcConnectionString, final String secret) + { + this.catalog = Validate.notBlank(catalog, "catalog must not be blank"); + this.type = Validate.notNull(type, "type must not be blank"); + this.jdbcConnectionString = Validate.notBlank(jdbcConnectionString, "jdbcConnectionString must not be blank"); + this.secret = Validate.notBlank(secret, "secret must not be blank"); + } + + public DatabaseConnectionConfig(final String catalog, final JdbcConnectionFactory.DatabaseEngine type, final String jdbcConnectionString) + { + this.catalog = Validate.notBlank(catalog, "catalog must not be blank"); + this.type = Validate.notNull(type, "type must not be blank"); + this.jdbcConnectionString = Validate.notBlank(jdbcConnectionString, "jdbcConnectionString must not be blank"); + } + + public JdbcConnectionFactory.DatabaseEngine getType() + { + return type; + } + + public String getJdbcConnectionString() + { + return jdbcConnectionString; + } + + public String getCatalog() + { + return catalog; + } + + public String getSecret() + { + return secret; + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DatabaseConnectionConfig that = (DatabaseConnectionConfig) o; + return Objects.equals(getCatalog(), that.getCatalog()) && + getType() == that.getType() && + Objects.equals(getJdbcConnectionString(), that.getJdbcConnectionString()) && + Objects.equals(getSecret(), that.getSecret()); + } + + @Override + public int hashCode() + { + return Objects.hash(getCatalog(), getType(), getJdbcConnectionString(), getSecret()); + } + + @Override + public String toString() + { + return "DatabaseConnectionConfig{" + + "catalog='" + catalog + '\'' + + ", type=" + type + + ", jdbcConnectionString='" + jdbcConnectionString + '\'' + + ", secret='" + secret + '\'' + + '}'; + } +} diff --git a/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/connection/DatabaseConnectionConfigBuilder.java b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/connection/DatabaseConnectionConfigBuilder.java new file mode 100644 index 0000000000..10d98120eb --- /dev/null +++ b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/connection/DatabaseConnectionConfigBuilder.java @@ -0,0 +1,145 @@ +/*- + * #%L + * athena-jdbc + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.jdbc.connection; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Builds database connection configuration per database instance. + */ +public class DatabaseConnectionConfigBuilder +{ + private static final String CONNECTION_STRING_PROPERTY_SUFFIX = "_connection_string"; + public static final String DEFAULT_CONNECTION_STRING_PROPERTY = "default"; + private static final int MUX_CATALOG_LIMIT = 100; + + private static final String CONNECTION_STRING_REGEX = "([a-zA-Z]+)://(.*)"; + private static final Pattern CONNECTION_STRING_PATTERN = Pattern.compile(CONNECTION_STRING_REGEX); + private static final String SECRET_PATTERN_STRING = "\\$\\{([a-zA-Z0-9/_+=.@-]+)}"; + public static final Pattern SECRET_PATTERN = Pattern.compile(SECRET_PATTERN_STRING); + + private Map properties; + + /** + * Utility to build database instance connection configurations from Environment variables. + * + * @return List of {@link DatabaseConnectionConfig} + */ + public static List buildFromSystemEnv() + { + return new DatabaseConnectionConfigBuilder() + .properties(System.getenv()) + .build(); + } + + /** + * Builder input all system properties. + * + * @param properties system properties + * @return {@link DatabaseConnectionConfigBuilder} + */ + public DatabaseConnectionConfigBuilder properties(final Map properties) + { + this.properties = properties; + return this; + } + + /** + * Builds Database instance configurations from input properties. + * + * @return List of {@link DatabaseConnectionConfig} + */ + public List build() + { + Validate.notEmpty(this.properties, "properties must not be empty"); + Validate.notBlank(this.properties.get(DEFAULT_CONNECTION_STRING_PROPERTY), "Default connection string must be present"); + + List databaseConnectionConfigs = new ArrayList<>(); + + int numberOfCatalogs = 0; + for (Map.Entry property : this.properties.entrySet()) { + final String key = property.getKey(); + final String value = property.getValue(); + + String catalogName; + if (DEFAULT_CONNECTION_STRING_PROPERTY.equals(key.toLowerCase())) { + catalogName = key.toLowerCase(); + } + else if (key.endsWith(CONNECTION_STRING_PROPERTY_SUFFIX)) { + catalogName = key.replace(CONNECTION_STRING_PROPERTY_SUFFIX, ""); + } + else { + // unknown property ignore + continue; + } + databaseConnectionConfigs.add(extractDatabaseConnectionConfig(catalogName, value)); + + numberOfCatalogs++; + if (numberOfCatalogs > MUX_CATALOG_LIMIT) { + throw new RuntimeException("Too many database instances in mux. Max supported is " + MUX_CATALOG_LIMIT); + } + } + + return databaseConnectionConfigs; + } + + private DatabaseConnectionConfig extractDatabaseConnectionConfig(final String catalogName, final String connectionString) + { + Matcher m = CONNECTION_STRING_PATTERN.matcher(connectionString); + final String dbType; + final String jdbcConnectionString; + if (m.find() && m.groupCount() == 2) { + dbType = m.group(1); + jdbcConnectionString = m.group(2); + } + else { + throw new RuntimeException("Invalid connection String for Catalog " + catalogName); + } + + Validate.notBlank(dbType, "Database type must not be blank."); + Validate.notBlank(jdbcConnectionString, "JDBC Connection string must not be blank."); + + JdbcConnectionFactory.DatabaseEngine databaseEngine = JdbcConnectionFactory.DatabaseEngine.valueOf(dbType.toUpperCase()); + + final Optional optionalSecretName = extractSecretName(jdbcConnectionString); + + return optionalSecretName.map(s -> new DatabaseConnectionConfig(catalogName, databaseEngine, jdbcConnectionString, s)) + .orElseGet(() -> new DatabaseConnectionConfig(catalogName, databaseEngine, jdbcConnectionString)); + } + + private Optional extractSecretName(final String jdbcConnectionString) + { + Matcher secretMatcher = SECRET_PATTERN.matcher(jdbcConnectionString); + String secretName = null; + if (secretMatcher.find() && secretMatcher.groupCount() == 1) { + secretName = secretMatcher.group(1); + } + + return StringUtils.isBlank(secretName) ? Optional.empty() : Optional.of(secretName); + } +} diff --git a/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/connection/DatabaseConnectionInfo.java b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/connection/DatabaseConnectionInfo.java new file mode 100644 index 0000000000..f64b8cf775 --- /dev/null +++ b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/connection/DatabaseConnectionInfo.java @@ -0,0 +1,66 @@ +/*- + * #%L + * athena-jdbc + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.jdbc.connection; + +import org.apache.commons.lang3.Validate; + +import java.util.Objects; + +public class DatabaseConnectionInfo +{ + private final String driverClassName; + private final int defaultPort; + + public DatabaseConnectionInfo(final String driverClassName, final int defaultPort) + { + this.driverClassName = Validate.notBlank(driverClassName, "driverClassName must not be blank"); + this.defaultPort = defaultPort; + } + + public String getDriverClassName() + { + return driverClassName; + } + + public int getDefaultPort() + { + return defaultPort; + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DatabaseConnectionInfo that = (DatabaseConnectionInfo) o; + return getDefaultPort() == that.getDefaultPort() && + Objects.equals(getDriverClassName(), that.getDriverClassName()); + } + + @Override + public int hashCode() + { + return Objects.hash(getDriverClassName(), getDefaultPort()); + } +} diff --git a/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/connection/GenericJdbcConnectionFactory.java b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/connection/GenericJdbcConnectionFactory.java new file mode 100644 index 0000000000..b85d856b2c --- /dev/null +++ b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/connection/GenericJdbcConnectionFactory.java @@ -0,0 +1,118 @@ +/*- + * #%L + * athena-jdbc + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.jdbc.connection; + +import com.google.common.collect.ImmutableMap; +import org.apache.commons.lang3.Validate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.Map; +import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Provides a generic jdbc connection factory that can be used to connect to standard databases. Configures following + * defaults if not present: + *

    + *
  • Default ports will be used for the engine if not present.
  • + *
+ */ +public class GenericJdbcConnectionFactory + implements JdbcConnectionFactory +{ + private static final Logger LOGGER = LoggerFactory.getLogger(GenericJdbcConnectionFactory.class); + + private static final String MYSQL_DRIVER_CLASS = "com.mysql.cj.jdbc.Driver"; + private static final int MYSQL_DEFAULT_PORT = 3306; + + private static final String POSTGRESQL_DRIVER_CLASS = "org.postgresql.Driver"; + private static final int POSTGRESQL_DEFAULT_PORT = 5432; + + private static final String REDSHIFT_DRIVER_CLASS = "com.amazon.redshift.jdbc.Driver"; + private static final int REDSHIFT_DEFAULT_PORT = 5439; + + private static final String ORACLE_DRIVER_CLASS = "oracle.jdbc.driver.OracleDriver"; + private static final int ORACLE_DEFAULT_PORT = 1521; + + private static final String SQL_SERVER_DRIVER_CLASS = "com.microsoft.sqlserver.jdbc.SQLServerDriver"; + private static final int SQL_SERVER_DEFAULT_PORT = 1433; + + private static final String SECRET_NAME_PATTERN_STRING = "(\\$\\{[a-zA-Z0-9/_+=.@-]+})"; + public static final Pattern SECRET_NAME_PATTERN = Pattern.compile(SECRET_NAME_PATTERN_STRING); + + private static final ImmutableMap CONNECTION_INFO = ImmutableMap.of( + DatabaseEngine.MYSQL, new DatabaseConnectionInfo(MYSQL_DRIVER_CLASS, MYSQL_DEFAULT_PORT), + DatabaseEngine.POSTGRES, new DatabaseConnectionInfo(POSTGRESQL_DRIVER_CLASS, POSTGRESQL_DEFAULT_PORT), + DatabaseEngine.REDSHIFT, new DatabaseConnectionInfo(REDSHIFT_DRIVER_CLASS, REDSHIFT_DEFAULT_PORT), + DatabaseEngine.ORACLE, new DatabaseConnectionInfo(ORACLE_DRIVER_CLASS, ORACLE_DEFAULT_PORT), + DatabaseEngine.SQLSERVER, new DatabaseConnectionInfo(SQL_SERVER_DRIVER_CLASS, SQL_SERVER_DEFAULT_PORT)); + + private final DatabaseConnectionConfig databaseConnectionConfig; + private final Properties jdbcProperties; + + public GenericJdbcConnectionFactory(final DatabaseConnectionConfig databaseConnectionConfig, final Map properties) + { + this.databaseConnectionConfig = Validate.notNull(databaseConnectionConfig, "databaseEngine must not be null"); + + this.jdbcProperties = new Properties(); + if (properties != null) { + this.jdbcProperties.putAll(properties); + } + } + + @Override + public Connection getConnection(final JdbcCredentialProvider jdbcCredentialProvider) + { + try { + DatabaseConnectionInfo databaseConnectionInfo = CONNECTION_INFO.get(this.databaseConnectionConfig.getType()); + + final String derivedJdbcString; + if (jdbcCredentialProvider != null) { + Matcher secretMatcher = SECRET_NAME_PATTERN.matcher(databaseConnectionConfig.getJdbcConnectionString()); + final String secretReplacement = String.format("user=%s&password=%s", jdbcCredentialProvider.getCredential().getUser(), + jdbcCredentialProvider.getCredential().getPassword()); + derivedJdbcString = secretMatcher.replaceAll(secretReplacement); + } + else { + derivedJdbcString = databaseConnectionConfig.getJdbcConnectionString(); + } + + // create connection string + LOGGER.info("Connection string {}", derivedJdbcString); + + // register driver + Class.forName(databaseConnectionInfo.getDriverClassName()).newInstance(); + + // create connection + return DriverManager.getConnection(derivedJdbcString, this.jdbcProperties); + } + catch (SQLException sqlException) { + throw new RuntimeException(sqlException.getErrorCode() + ": " + sqlException); + } + catch (ClassNotFoundException | IllegalAccessException | InstantiationException ex) { + throw new RuntimeException(ex); + } + } +} diff --git a/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/connection/JdbcConnectionFactory.java b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/connection/JdbcConnectionFactory.java new file mode 100644 index 0000000000..0e264346fc --- /dev/null +++ b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/connection/JdbcConnectionFactory.java @@ -0,0 +1,60 @@ +/*- + * #%L + * athena-jdbc + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.jdbc.connection; + +import java.sql.Connection; + +/** + * Factory abstracts creation of JDBC connection to database. + */ +public interface JdbcConnectionFactory +{ + /** + * Used to connect with standard databases. Default port for the engine will be used if value is negative. + * + * @param jdbcCredentialProvider jdbc user and password provider + * @return {@link Connection} + */ + Connection getConnection(JdbcCredentialProvider jdbcCredentialProvider); + + /** + * Databases supported to create JDBC connection. These would be connector names as well. + */ + enum DatabaseEngine + { + MYSQL("mysql"), + POSTGRES("postgres"), + ORACLE("oracle"), + SQLSERVER("sqlserver"), + REDSHIFT("redshift"); + + private final String dbName; + + DatabaseEngine(final String dbName) + { + this.dbName = dbName; + } + + public String getDbName() + { + return this.dbName; + } + } +} diff --git a/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/connection/JdbcCredential.java b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/connection/JdbcCredential.java new file mode 100644 index 0000000000..b188a3370a --- /dev/null +++ b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/connection/JdbcCredential.java @@ -0,0 +1,69 @@ +/*- + * #%L + * athena-jdbc + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.jdbc.connection; + +import org.apache.commons.lang3.Validate; + +import java.util.Objects; + +/** + * Used to store Jdbc user and password information. + */ +public class JdbcCredential +{ + private final String user; + private final String password; + + public JdbcCredential(String user, String password) + { + this.user = Validate.notBlank(user, "User must not be blank"); + this.password = Validate.notBlank(password, "Password must not be blank"); + } + + public String getUser() + { + return user; + } + + public String getPassword() + { + return password; + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + JdbcCredential that = (JdbcCredential) o; + return Objects.equals(getUser(), that.getUser()) && + Objects.equals(getPassword(), that.getPassword()); + } + + @Override + public int hashCode() + { + return Objects.hash(getUser(), getPassword()); + } +} diff --git a/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/connection/JdbcCredentialProvider.java b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/connection/JdbcCredentialProvider.java new file mode 100644 index 0000000000..161c0a4c68 --- /dev/null +++ b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/connection/JdbcCredentialProvider.java @@ -0,0 +1,33 @@ +/*- + * #%L + * athena-jdbc + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.jdbc.connection; + +/** + * JDBC username and password provider. + */ +public interface JdbcCredentialProvider +{ + /** + * Retrieves credential for database. + * + * @return {@link JdbcCredential} + */ + JdbcCredential getCredential(); +} diff --git a/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/connection/RdsSecretsCredentialProvider.java b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/connection/RdsSecretsCredentialProvider.java new file mode 100644 index 0000000000..199fa62e3f --- /dev/null +++ b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/connection/RdsSecretsCredentialProvider.java @@ -0,0 +1,56 @@ +/*- + * #%L + * athena-jdbc + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.jdbc.connection; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class RdsSecretsCredentialProvider + implements JdbcCredentialProvider +{ + private static final Logger LOGGER = LoggerFactory.getLogger(RdsSecretsCredentialProvider.class); + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + private final JdbcCredential jdbcCredential; + + public RdsSecretsCredentialProvider(final String secretString) + { + Map rdsSecrets; + try { + rdsSecrets = OBJECT_MAPPER.readValue(secretString, HashMap.class); + } + catch (IOException ioException) { + throw new RuntimeException("Could not deserialize RDS credentials into HashMap", ioException); + } + + this.jdbcCredential = new JdbcCredential(rdsSecrets.get("username"), rdsSecrets.get("password")); + } + + @Override + public JdbcCredential getCredential() + { + return this.jdbcCredential; + } +} diff --git a/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/connection/StaticJdbcCredentialProvider.java b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/connection/StaticJdbcCredentialProvider.java new file mode 100644 index 0000000000..bf3a5326f8 --- /dev/null +++ b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/connection/StaticJdbcCredentialProvider.java @@ -0,0 +1,44 @@ +/*- + * #%L + * athena-jdbc + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.jdbc.connection; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; + +public class StaticJdbcCredentialProvider + implements JdbcCredentialProvider +{ + private final JdbcCredential jdbcCredential; + + public StaticJdbcCredentialProvider(final JdbcCredential jdbcCredential) + { + this.jdbcCredential = Validate.notNull(jdbcCredential, "jdbcCredential must not be null."); + + if (StringUtils.isAnyBlank(jdbcCredential.getUser(), jdbcCredential.getPassword())) { + throw new RuntimeException("User or password must not be blank."); + } + } + + @Override + public JdbcCredential getCredential() + { + return this.jdbcCredential; + } +} diff --git a/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/manager/JDBCUtil.java b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/manager/JDBCUtil.java new file mode 100644 index 0000000000..27e2f28299 --- /dev/null +++ b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/manager/JDBCUtil.java @@ -0,0 +1,151 @@ +/*- + * #%L + * athena-jdbc + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.jdbc.manager; + +import com.amazonaws.connectors.athena.jdbc.connection.DatabaseConnectionConfig; +import com.amazonaws.connectors.athena.jdbc.connection.DatabaseConnectionConfigBuilder; +import com.amazonaws.connectors.athena.jdbc.connection.JdbcConnectionFactory; +import com.amazonaws.connectors.athena.jdbc.mysql.MySqlMetadataHandler; +import com.amazonaws.connectors.athena.jdbc.mysql.MySqlRecordHandler; +import com.amazonaws.connectors.athena.jdbc.postgresql.PostGreSqlMetadataHandler; +import com.amazonaws.connectors.athena.jdbc.postgresql.PostGreSqlRecordHandler; +import com.google.common.collect.ImmutableMap; +import org.apache.commons.lang3.Validate; + +import java.util.List; +import java.util.Map; + +public final class JDBCUtil +{ + private static final String DEFAULT_CATALOG_PREFIX = "lambda:"; + private static final String LAMBDA_FUNCTION_NAME_PROPERTY = "AWS_LAMBDA_FUNCTION_NAME"; + private JDBCUtil() {} + + public static DatabaseConnectionConfig getSingleDatabaseConfigFromEnv(final JdbcConnectionFactory.DatabaseEngine databaseEngine) + { + List databaseConnectionConfigs = DatabaseConnectionConfigBuilder.buildFromSystemEnv(); + + for (DatabaseConnectionConfig databaseConnectionConfig : databaseConnectionConfigs) { + if (DatabaseConnectionConfigBuilder.DEFAULT_CONNECTION_STRING_PROPERTY.equals(databaseConnectionConfig.getCatalog())) { + return databaseConnectionConfig; + } + } + + throw new RuntimeException("Must provide default connection string parameter " + DatabaseConnectionConfigBuilder.DEFAULT_CONNECTION_STRING_PROPERTY); + } + + /** + * Creates a map of Catalog to respective metadata handler to be used by Multiplexer. + * + * @param properties system properties. + * @return Map of String -> {@link JdbcMetadataHandler} + */ + public static Map createJdbcMetadataHandlerMap(final Map properties) + { + ImmutableMap.Builder metadataHandlerMap = ImmutableMap.builder(); + + final String functionName = Validate.notBlank(properties.get(LAMBDA_FUNCTION_NAME_PROPERTY), "Lambda function name not present in environment."); + List databaseConnectionConfigs = new DatabaseConnectionConfigBuilder().properties(properties).build(); + + if (databaseConnectionConfigs.isEmpty()) { + throw new RuntimeException("At least one connection string required."); + } + + boolean defaultPresent = false; + + for (DatabaseConnectionConfig databaseConnectionConfig : databaseConnectionConfigs) { + JdbcMetadataHandler jdbcMetadataHandler = createJdbcMetadataHandler(databaseConnectionConfig); + metadataHandlerMap.put(databaseConnectionConfig.getCatalog(), jdbcMetadataHandler); + + if (DatabaseConnectionConfigBuilder.DEFAULT_CONNECTION_STRING_PROPERTY.equals(databaseConnectionConfig.getCatalog())) { + metadataHandlerMap.put(DEFAULT_CATALOG_PREFIX + functionName, jdbcMetadataHandler); + defaultPresent = true; + } + } + + if (!defaultPresent) { + throw new RuntimeException("Must provide connection parameters for default database instance " + DatabaseConnectionConfigBuilder.DEFAULT_CONNECTION_STRING_PROPERTY); + } + + return metadataHandlerMap.build(); + } + + private static JdbcMetadataHandler createJdbcMetadataHandler(final DatabaseConnectionConfig databaseConnectionConfig) + { + switch (databaseConnectionConfig.getType()) { + case MYSQL: + return new MySqlMetadataHandler(databaseConnectionConfig); + case REDSHIFT: + case POSTGRES: + return new PostGreSqlMetadataHandler(databaseConnectionConfig); + default: + throw new RuntimeException("Mux: Unhandled database engine " + databaseConnectionConfig.getType()); + } + } + + /** + * Creates a map of Catalog to respective record handler to be used by Multiplexer. + * + * @param properties system properties. + * @return Map of String -> {@link JdbcRecordHandler} + */ + public static Map createJdbcRecordHandlerMap(final Map properties) + { + ImmutableMap.Builder recordHandlerMap = ImmutableMap.builder(); + + final String functionName = Validate.notBlank(properties.get(LAMBDA_FUNCTION_NAME_PROPERTY), "Lambda function name not present in environment."); + List databaseConnectionConfigs = new DatabaseConnectionConfigBuilder().properties(properties).build(); + + if (databaseConnectionConfigs.isEmpty()) { + throw new RuntimeException("At least one connection string required."); + } + + boolean defaultPresent = false; + + for (DatabaseConnectionConfig databaseConnectionConfig : databaseConnectionConfigs) { + JdbcRecordHandler jdbcRecordHandler = createJdbcRecordHandler(databaseConnectionConfig); + recordHandlerMap.put(databaseConnectionConfig.getCatalog(), jdbcRecordHandler); + + if (DatabaseConnectionConfigBuilder.DEFAULT_CONNECTION_STRING_PROPERTY.equals(databaseConnectionConfig.getCatalog())) { + recordHandlerMap.put(DEFAULT_CATALOG_PREFIX + functionName, jdbcRecordHandler); + defaultPresent = true; + } + } + + if (!defaultPresent) { + throw new RuntimeException("Must provide connection parameters for default database instance " + DatabaseConnectionConfigBuilder.DEFAULT_CONNECTION_STRING_PROPERTY); + } + + return recordHandlerMap.build(); + } + + private static JdbcRecordHandler createJdbcRecordHandler(final DatabaseConnectionConfig databaseConnectionConfig) + { + switch (databaseConnectionConfig.getType()) { + case MYSQL: + return new MySqlRecordHandler(databaseConnectionConfig); + case REDSHIFT: + case POSTGRES: + return new PostGreSqlRecordHandler(databaseConnectionConfig); + default: + throw new RuntimeException("Mux: Unhandled database engine " + databaseConnectionConfig.getType()); + } + } +} diff --git a/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/manager/JdbcArrowTypeConverter.java b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/manager/JdbcArrowTypeConverter.java new file mode 100644 index 0000000000..84811c4e71 --- /dev/null +++ b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/manager/JdbcArrowTypeConverter.java @@ -0,0 +1,40 @@ +/*- + * #%L + * athena-jdbc + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.jdbc.manager; + +import org.apache.arrow.adapter.jdbc.JdbcFieldInfo; +import org.apache.arrow.adapter.jdbc.JdbcToArrowUtils; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class JdbcArrowTypeConverter +{ + private static final Logger LOGGER = LoggerFactory.getLogger(JdbcMetadataHandler.class); + + private JdbcArrowTypeConverter() {} + + public static ArrowType toArrowType(final int jdbcType, final int precision, final int scale) + { + return JdbcToArrowUtils.getArrowTypeForJdbcField( + new JdbcFieldInfo(jdbcType, precision, scale), + null); + } +} diff --git a/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/manager/JdbcMetadataHandler.java b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/manager/JdbcMetadataHandler.java new file mode 100644 index 0000000000..64950343d4 --- /dev/null +++ b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/manager/JdbcMetadataHandler.java @@ -0,0 +1,260 @@ +/*- + * #%L + * athena-jdbc + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.jdbc.manager; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockWriter; +import com.amazonaws.athena.connector.lambda.data.FieldBuilder; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.handlers.MetadataHandler; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesResponse; +import com.amazonaws.connectors.athena.jdbc.connection.DatabaseConnectionConfig; +import com.amazonaws.connectors.athena.jdbc.connection.JdbcConnectionFactory; +import com.amazonaws.connectors.athena.jdbc.connection.JdbcCredentialProvider; +import com.amazonaws.connectors.athena.jdbc.connection.RdsSecretsCredentialProvider; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.arrow.vector.types.pojo.Schema; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +public abstract class JdbcMetadataHandler + extends MetadataHandler +{ + private static final Logger LOGGER = LoggerFactory.getLogger(JdbcMetadataHandler.class); + private final JdbcConnectionFactory jdbcConnectionFactory; + private final DatabaseConnectionConfig databaseConnectionConfig; + + /** + * Used only by Multiplexing handler. All calls will be delegated to respective database handler. + */ + protected JdbcMetadataHandler() + { + super(null); + this.jdbcConnectionFactory = null; + this.databaseConnectionConfig = null; + } + + protected JdbcMetadataHandler(final DatabaseConnectionConfig databaseConnectionConfig, final JdbcConnectionFactory jdbcConnectionFactory) + { + super(databaseConnectionConfig.getType().getDbName()); + this.jdbcConnectionFactory = Validate.notNull(jdbcConnectionFactory, "jdbcConnectionFactory must not be null"); + + this.databaseConnectionConfig = Validate.notNull(databaseConnectionConfig, "databaseConnectionConfig must not be null"); + } + + @VisibleForTesting + protected JdbcMetadataHandler(final DatabaseConnectionConfig databaseConnectionConfig, final AWSSecretsManager secretsManager, + final AmazonAthena athena, final JdbcConnectionFactory jdbcConnectionFactory) + { + super(null, secretsManager, athena, databaseConnectionConfig.getType().getDbName(), null, null); + this.jdbcConnectionFactory = Validate.notNull(jdbcConnectionFactory, "jdbcConnectionFactory must not be null"); + this.databaseConnectionConfig = Validate.notNull(databaseConnectionConfig, "databaseConnectionConfig must not be null"); + } + + protected JdbcConnectionFactory getJdbcConnectionFactory() + { + return jdbcConnectionFactory; + } + + protected JdbcCredentialProvider getCredentialProvider() + { + final String secretName = databaseConnectionConfig.getSecret(); + if (StringUtils.isNotBlank(secretName)) { + LOGGER.info("Using Secrets Manager."); + return new RdsSecretsCredentialProvider(getSecret(secretName)); + } + + return null; + } + + @Override + public ListSchemasResponse doListSchemaNames(final BlockAllocator blockAllocator, final ListSchemasRequest listSchemasRequest) + { + try (Connection connection = jdbcConnectionFactory.getConnection(getCredentialProvider())) { + LOGGER.info("{}: List schema names for Catalog {}", listSchemasRequest.getQueryId(), listSchemasRequest.getCatalogName()); + return new ListSchemasResponse(listSchemasRequest.getCatalogName(), listDatabaseNames(connection)); + } + catch (SQLException sqlException) { + throw new RuntimeException(sqlException.getErrorCode() + ": " + sqlException.getMessage()); + } + } + + private Set listDatabaseNames(final Connection jdbcConnection) + throws SQLException + { + try (ResultSet resultSet = jdbcConnection.getMetaData().getSchemas()) { + ImmutableSet.Builder schemaNames = ImmutableSet.builder(); + while (resultSet.next()) { + String schemaName = resultSet.getString("TABLE_SCHEM"); + // skip internal schemas + if (!schemaName.equals("information_schema")) { + schemaNames.add(schemaName); + } + } + return schemaNames.build(); + } + } + + @Override + public ListTablesResponse doListTables(final BlockAllocator blockAllocator, final ListTablesRequest listTablesRequest) + { + try (Connection connection = jdbcConnectionFactory.getConnection(getCredentialProvider())) { + LOGGER.info("{}: List table names for Catalog {}, Table {}", listTablesRequest.getQueryId(), listTablesRequest.getCatalogName(), listTablesRequest.getSchemaName()); + return new ListTablesResponse(listTablesRequest.getCatalogName(), listTables(connection, listTablesRequest.getSchemaName())); + } + catch (SQLException sqlException) { + throw new RuntimeException(sqlException.getErrorCode() + ": " + sqlException.getMessage()); + } + } + + private List listTables(final Connection jdbcConnection, final String databaseName) + throws SQLException + { + try (ResultSet resultSet = getTables(jdbcConnection, databaseName)) { + ImmutableList.Builder list = ImmutableList.builder(); + while (resultSet.next()) { + list.add(getSchemaTableName(resultSet)); + } + return list.build(); + } + } + + private ResultSet getTables(final Connection connection, final String schemaName) + throws SQLException + { + DatabaseMetaData metadata = connection.getMetaData(); + String escape = metadata.getSearchStringEscape(); + return metadata.getTables( + connection.getCatalog(), + escapeNamePattern(schemaName, escape), + null, + new String[] {"TABLE", "VIEW"}); + } + + private TableName getSchemaTableName(final ResultSet resultSet) + throws SQLException + { + return new TableName( + resultSet.getString("TABLE_SCHEM"), + resultSet.getString("TABLE_NAME")); + } + + protected String escapeNamePattern(final String name, final String escape) + { + if ((name == null) || (escape == null)) { + return name; + } + Preconditions.checkArgument(!escape.equals("_"), "Escape string must not be '_'"); + Preconditions.checkArgument(!escape.equals("%"), "Escape string must not be '%'"); + String escapedName = name.replace(escape, escape + escape); + escapedName = escapedName.replace("_", escape + "_"); + escapedName = escapedName.replace("%", escape + "%"); + return escapedName; + } + + @Override + public GetTableResponse doGetTable(final BlockAllocator blockAllocator, final GetTableRequest getTableRequest) + { + try (Connection connection = jdbcConnectionFactory.getConnection(getCredentialProvider())) { + Schema partitionSchema = getPartitionSchema(getTableRequest.getCatalogName()); + return new GetTableResponse(getTableRequest.getCatalogName(), getTableRequest.getTableName(), getSchema(connection, getTableRequest.getTableName(), partitionSchema), + partitionSchema.getFields().stream().map(Field::getName).collect(Collectors.toSet())); + } + catch (SQLException sqlException) { + throw new RuntimeException(sqlException.getErrorCode() + ": " + sqlException.getMessage()); + } + } + + private Schema getSchema(Connection jdbcConnection, TableName tableName, Schema partitionSchema) + throws SQLException + { + SchemaBuilder schemaBuilder = SchemaBuilder.newBuilder(); + + try (ResultSet resultSet = getColumns(jdbcConnection.getCatalog(), tableName, jdbcConnection.getMetaData())) { + boolean found = false; + while (resultSet.next()) { + found = true; + ArrowType columnType = JdbcArrowTypeConverter.toArrowType( + resultSet.getInt("DATA_TYPE"), + resultSet.getInt("COLUMN_SIZE"), + resultSet.getInt("DECIMAL_DIGITS")); + String columnName = resultSet.getString("COLUMN_NAME"); + schemaBuilder.addField(FieldBuilder.newBuilder(columnName, columnType).build()); + } + if (!found) { + throw new RuntimeException("Could not find table in " + tableName.getSchemaName()); + } + + // add partition columns + partitionSchema.getFields().forEach(schemaBuilder::addField); + + return schemaBuilder.build(); + } + } + + private ResultSet getColumns(final String catalogName, final TableName tableHandle, final DatabaseMetaData metadata) + throws SQLException + { + String escape = metadata.getSearchStringEscape(); + return metadata.getColumns( + catalogName, + escapeNamePattern(tableHandle.getSchemaName(), escape), + escapeNamePattern(tableHandle.getTableName(), escape), + null); + } + + public abstract Schema getPartitionSchema(final String catalogName); + + @Override + public abstract void getPartitions( + final BlockWriter blockWriter, + final GetTableLayoutRequest request, QueryStatusChecker queryStatusChecker) + throws Exception; + + @Override + public abstract GetSplitsResponse doGetSplits(BlockAllocator blockAllocator, GetSplitsRequest getSplitsRequest); +} diff --git a/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/manager/JdbcRecordHandler.java b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/manager/JdbcRecordHandler.java new file mode 100644 index 0000000000..a0292fef2b --- /dev/null +++ b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/manager/JdbcRecordHandler.java @@ -0,0 +1,183 @@ +/*- + * #%L + * athena-jdbc + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.jdbc.manager; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockSpiller; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.handlers.RecordHandler; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.connectors.athena.jdbc.connection.DatabaseConnectionConfig; +import com.amazonaws.connectors.athena.jdbc.connection.JdbcConnectionFactory; +import com.amazonaws.connectors.athena.jdbc.connection.JdbcCredentialProvider; +import com.amazonaws.connectors.athena.jdbc.connection.RdsSecretsCredentialProvider; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.google.common.collect.ImmutableMap; +import org.apache.arrow.adapter.jdbc.JdbcFieldInfo; +import org.apache.arrow.adapter.jdbc.JdbcToArrowUtils; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.arrow.vector.types.pojo.Schema; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.util.Calendar; +import java.util.HashMap; +import java.util.Map; + +public abstract class JdbcRecordHandler + extends RecordHandler +{ + private static final Logger LOGGER = LoggerFactory.getLogger(JdbcRecordHandler.class); + // TODO support all data types + private static final ImmutableMap> VALUE_EXTRACTOR = new ImmutableMap.Builder>() + .put(Types.MinorType.BIT, ResultSet::getBoolean) + .put(Types.MinorType.TINYINT, ResultSet::getByte) + .put(Types.MinorType.SMALLINT, ResultSet::getShort) + .put(Types.MinorType.INT, ResultSet::getInt) + .put(Types.MinorType.BIGINT, ResultSet::getLong) + .put(Types.MinorType.FLOAT4, ResultSet::getFloat) + .put(Types.MinorType.FLOAT8, ResultSet::getDouble) + .put(Types.MinorType.DATEDAY, ResultSet::getDate) + .put(Types.MinorType.DATEMILLI, ResultSet::getTimestamp) + .put(Types.MinorType.VARCHAR, ResultSet::getString) + .put(Types.MinorType.VARBINARY, ResultSet::getBytes) + .put(Types.MinorType.DECIMAL, ResultSet::getBigDecimal) + .build(); + private final JdbcConnectionFactory jdbcConnectionFactory; + private final DatabaseConnectionConfig databaseConnectionConfig; + + /** + * Used only by Multiplexing handler. All invocations will be delegated to respective database handler. + */ + protected JdbcRecordHandler() + { + super(null); + this.jdbcConnectionFactory = null; + this.databaseConnectionConfig = null; + } + + protected JdbcRecordHandler(final AmazonS3 amazonS3, final AWSSecretsManager secretsManager, AmazonAthena athena, final DatabaseConnectionConfig databaseConnectionConfig, + final JdbcConnectionFactory jdbcConnectionFactory) + { + super(amazonS3, secretsManager, athena, databaseConnectionConfig.getType().getDbName()); + this.jdbcConnectionFactory = Validate.notNull(jdbcConnectionFactory, "jdbcConnectionFactory must not be null"); + this.databaseConnectionConfig = Validate.notNull(databaseConnectionConfig, "databaseConnectionConfig must not be null"); + } + + private JdbcCredentialProvider getCredentialProvider() + { + final String secretName = this.databaseConnectionConfig.getSecret(); + if (StringUtils.isNotBlank(secretName)) { + return new RdsSecretsCredentialProvider(getSecret(secretName)); + } + + return null; + } + + @Override + public void readWithConstraint(BlockSpiller blockSpiller, ReadRecordsRequest readRecordsRequest, QueryStatusChecker queryStatusChecker) + { + LOGGER.info("{}: Catalog: {}, table {}, splits {}", readRecordsRequest.getQueryId(), readRecordsRequest.getCatalogName(), readRecordsRequest.getTableName(), + readRecordsRequest.getSplit().getProperties()); + try (Connection connection = this.jdbcConnectionFactory.getConnection(getCredentialProvider()); + PreparedStatement preparedStatement = buildSplitSql(connection, readRecordsRequest.getCatalogName(), readRecordsRequest.getTableName(), readRecordsRequest.getSchema(), + readRecordsRequest.getConstraints(), readRecordsRequest.getSplit()); + ResultSet resultSet = preparedStatement.executeQuery()) { + ResultSetMetaData resultSetMetaData = resultSet.getMetaData(); + + Map partitionValues = readRecordsRequest.getSplit().getProperties(); + + final Map typeMap = new HashMap<>(); + int columnIndex = 1; + for (Field nextField : readRecordsRequest.getSchema().getFields()) { + if (partitionValues.containsKey(nextField.getName())) { + continue; //ignore partition columns + } + Types.MinorType minorTypeForArrowType = Types.getMinorTypeForArrowType(JdbcToArrowUtils.getArrowTypeForJdbcField(new JdbcFieldInfo(resultSetMetaData, columnIndex), + Calendar.getInstance())); + typeMap.put(nextField.getName(), minorTypeForArrowType); + columnIndex++; + } + + while (resultSet.next()) { + if (!queryStatusChecker.isQueryRunning()) { + return; + } + blockSpiller.writeRows((Block block, int rowNum) -> { + try { + boolean matched; + for (Field nextField : readRecordsRequest.getSchema().getFields()) { + Object value; + if (partitionValues.containsKey(nextField.getName())) { + value = partitionValues.get(nextField.getName()); + } + else { + value = getArrowValue(resultSet, nextField.getName(), typeMap.get(nextField.getName())); + } + matched = block.offerValue(nextField.getName(), rowNum, value); + if (!matched) { + return 0; + } + } + + return 1; + } + catch (SQLException sqlException) { + throw new RuntimeException(sqlException.getErrorCode() + ": " + sqlException.getMessage(), sqlException); + } + }); + } + } + catch (SQLException sqlException) { + throw new RuntimeException(sqlException.getErrorCode() + ": " + sqlException.getMessage(), sqlException); + } + } + + private Object getArrowValue(final ResultSet resultSet, final String columnName, final Types.MinorType minorType) + throws SQLException + { + return VALUE_EXTRACTOR.getOrDefault(minorType, (rs, col) -> { + throw new RuntimeException("Unhandled column type " + minorType); + }) + .call(resultSet, columnName); + } + + public abstract PreparedStatement buildSplitSql(Connection jdbcConnection, String catalogName, TableName tableName, Schema schema, Constraints constraints, Split split) + throws SQLException; + + private interface ResultSetValueExtractor + { + T call(ResultSet resultSet, String columnName) + throws SQLException; + } +} diff --git a/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/manager/JdbcSplitQueryBuilder.java b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/manager/JdbcSplitQueryBuilder.java new file mode 100644 index 0000000000..d5e6485393 --- /dev/null +++ b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/manager/JdbcSplitQueryBuilder.java @@ -0,0 +1,289 @@ +/*- + * #%L + * athena-jdbc + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.jdbc.manager; + +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.domain.predicate.Range; +import com.amazonaws.athena.connector.lambda.domain.predicate.SortedRangeSet; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import com.google.common.base.Joiner; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.arrow.vector.types.pojo.Schema; +import org.apache.commons.lang3.Validate; +import org.joda.time.DateTimeZone; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.math.BigDecimal; +import java.sql.Connection; +import java.sql.Date; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +/** + * Query builder for DB split. + */ +public abstract class JdbcSplitQueryBuilder +{ + private static final Logger LOGGER = LoggerFactory.getLogger(JdbcSplitQueryBuilder.class); + + private static final int MILLIS_SHIFT = 12; + + private final String quoteCharacters; + + public JdbcSplitQueryBuilder(String quoteCharacters) + { + this.quoteCharacters = Validate.notBlank(quoteCharacters, "quoteCharacters must not be blank"); + } + + public PreparedStatement buildSql( + final Connection jdbcConnection, + final String catalog, + final String schema, + final String table, + final Schema tableSchema, + final Constraints constraints, + final Split split) + throws SQLException + { + StringBuilder sql = new StringBuilder(); + + String columnNames = tableSchema.getFields().stream() + .map(Field::getName) + .filter(c -> !split.getProperties().containsKey(c)) + .map(this::quote) + .collect(Collectors.joining(", ")); + + sql.append("SELECT "); + sql.append(columnNames); + if (columnNames.isEmpty()) { + sql.append("null"); + } + + sql.append(getFromClauseWithSplit(catalog, schema, table, split)); + + List accumulator = new ArrayList<>(); + + List clauses = toConjuncts(tableSchema.getFields(), constraints, accumulator, split.getProperties()); + if (!clauses.isEmpty()) { + sql.append(" WHERE ") + .append(Joiner.on(" AND ").join(clauses)); + } + + PreparedStatement statement = jdbcConnection.prepareStatement(sql.toString()); + + // TODO all types, converts Arrow values to JDBC. + for (int i = 0; i < accumulator.size(); i++) { + TypeAndValue typeAndValue = accumulator.get(i); + + Types.MinorType minorTypeForArrowType = Types.getMinorTypeForArrowType(typeAndValue.getType()); + + switch (minorTypeForArrowType) { + case BIGINT: + statement.setLong(i + 1, (long) typeAndValue.getValue()); + break; + case INT: + statement.setInt(i + 1, ((Number) typeAndValue.getValue()).intValue()); + break; + case SMALLINT: + statement.setShort(i + 1, ((Number) typeAndValue.getValue()).shortValue()); + break; + case TINYINT: + statement.setByte(i + 1, ((Number) typeAndValue.getValue()).byteValue()); + break; + case FLOAT8: + statement.setDouble(i + 1, (double) typeAndValue.getValue()); + break; + case FLOAT4: + statement.setFloat(i + 1, (float) typeAndValue.getValue()); + break; + case BIT: + statement.setBoolean(i + 1, (boolean) typeAndValue.getValue()); + break; + case DATEDAY: + long millis = TimeUnit.DAYS.toMillis((long) typeAndValue.getValue()); + statement.setDate(i + 1, new Date(DateTimeZone.UTC.getMillisKeepLocal(DateTimeZone.getDefault(), millis))); + break; + case DATEMILLI: + statement.setTimestamp(i + 1, new Timestamp((long) typeAndValue.getValue())); + break; + case VARCHAR: + statement.setString(i + 1, String.valueOf(typeAndValue.getValue())); + break; + case VARBINARY: + statement.setBytes(i + 1, (byte[]) typeAndValue.getValue()); + break; + case DECIMAL: + ArrowType.Decimal decimalType = (ArrowType.Decimal) typeAndValue.getType(); + statement.setBigDecimal(i + 1, BigDecimal.valueOf((long) typeAndValue.getValue(), decimalType.getScale())); + break; + default: + throw new UnsupportedOperationException(String.format("Can't handle type: %s, %s", typeAndValue.getType(), minorTypeForArrowType)); + } + } + + return statement; + } + + protected abstract String getFromClauseWithSplit(final String catalog, final String schema, final String table, final Split split); + + private List toConjuncts(List columns, Constraints constraints, List accumulator, Map partitionSplit) + { + ImmutableList.Builder builder = ImmutableList.builder(); + for (Field column : columns) { + if (partitionSplit.containsKey(column.getName())) { + continue; // Ignore constraints on partition name as RDBMS does not contain these as columns. Presto will filter these values. + } + ArrowType type = column.getType(); + if (constraints.getSummary() != null && !constraints.getSummary().isEmpty()) { + ValueSet valueSet = constraints.getSummary().get(column.getName()); + if (valueSet != null) { + builder.add(toPredicate(column.getName(), valueSet, type, accumulator)); + } + } + } + return builder.build(); + } + + private String toPredicate(String columnName, ValueSet valueSet, ArrowType type, List accumulator) + { + List disjuncts = new ArrayList<>(); + List singleValues = new ArrayList<>(); + + // TODO Add isNone and isAll checks once we have data on nullability. + + if (valueSet instanceof SortedRangeSet) { + if (valueSet.isNone() && valueSet.isNullAllowed()) { + return String.format("(%s IS NULL)", columnName); + } + + if (valueSet.isNullAllowed()) { + disjuncts.add(String.format("(%s IS NULL)", columnName)); + } + + Range rangeSpan = ((SortedRangeSet) valueSet).getSpan(); + if (!valueSet.isNullAllowed() && rangeSpan.getLow().isLowerUnbounded() && rangeSpan.getHigh().isUpperUnbounded()) { + return String.format("(%s IS NOT NULL)", columnName); + } + + for (Range range : valueSet.getRanges().getOrderedRanges()) { + if (range.isSingleValue()) { + singleValues.add(range.getLow().getValue()); + } + else { + List rangeConjuncts = new ArrayList<>(); + if (!range.getLow().isLowerUnbounded()) { + switch (range.getLow().getBound()) { + case ABOVE: + rangeConjuncts.add(toPredicate(columnName, ">", range.getLow().getValue(), type, accumulator)); + break; + case EXACTLY: + rangeConjuncts.add(toPredicate(columnName, ">=", range.getLow().getValue(), type, accumulator)); + break; + case BELOW: + throw new IllegalArgumentException("Low marker should never use BELOW bound"); + default: + throw new AssertionError("Unhandled bound: " + range.getLow().getBound()); + } + } + if (!range.getHigh().isUpperUnbounded()) { + switch (range.getHigh().getBound()) { + case ABOVE: + throw new IllegalArgumentException("High marker should never use ABOVE bound"); + case EXACTLY: + rangeConjuncts.add(toPredicate(columnName, "<=", range.getHigh().getValue(), type, accumulator)); + break; + case BELOW: + rangeConjuncts.add(toPredicate(columnName, "<", range.getHigh().getValue(), type, accumulator)); + break; + default: + throw new AssertionError("Unhandled bound: " + range.getHigh().getBound()); + } + } + // If rangeConjuncts is null, then the range was ALL, which should already have been checked for + Preconditions.checkState(!rangeConjuncts.isEmpty()); + disjuncts.add("(" + Joiner.on(" AND ").join(rangeConjuncts) + ")"); + } + } + + // Add back all of the possible single values either as an equality or an IN predicate + if (singleValues.size() == 1) { + disjuncts.add(toPredicate(columnName, "=", Iterables.getOnlyElement(singleValues), type, accumulator)); + } + else if (singleValues.size() > 1) { + for (Object value : singleValues) { + accumulator.add(new TypeAndValue(type, value)); + } + String values = Joiner.on(",").join(Collections.nCopies(singleValues.size(), "?")); + disjuncts.add(quote(columnName) + " IN (" + values + ")"); + } + } + + return "(" + Joiner.on(" OR ").join(disjuncts) + ")"; + } + + private String toPredicate(String columnName, String operator, Object value, ArrowType type, + List accumulator) + { + accumulator.add(new TypeAndValue(type, value)); + return quote(columnName) + " " + operator + " ?"; + } + + protected String quote(String name) + { + name = name.replace(quoteCharacters, quoteCharacters + quoteCharacters); + return quoteCharacters + name + quoteCharacters; + } + + private static class TypeAndValue + { + private final ArrowType type; + private final Object value; + + TypeAndValue(ArrowType type, Object value) + { + this.type = Validate.notNull(type, "type is null"); + this.value = Validate.notNull(value, "value is null"); + } + + ArrowType getType() + { + return type; + } + + Object getValue() + { + return value; + } + } +} diff --git a/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/manager/PreparedStatementBuilder.java b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/manager/PreparedStatementBuilder.java new file mode 100644 index 0000000000..e1ab8a8a1f --- /dev/null +++ b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/manager/PreparedStatementBuilder.java @@ -0,0 +1,79 @@ +/*- + * #%L + * athena-jdbc + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.jdbc.manager; + +import org.apache.commons.lang3.Validate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.List; + +public class PreparedStatementBuilder +{ + private static final Logger LOGGER = LoggerFactory.getLogger(PreparedStatementBuilder.class); + + private String query; + private List parameters; + private Connection connection; + + public PreparedStatementBuilder withParameters(final List parameters) + { + this.parameters = Validate.notEmpty(parameters, "parameters must not be null"); + return this; + } + + public PreparedStatementBuilder withQuery(final String query) + { + this.query = Validate.notBlank(query, "query must not be blank"); + return this; + } + + public PreparedStatementBuilder withConnection(final Connection connection) + { + this.connection = Validate.notNull(connection, "connection must not be null"); + return this; + } + + public PreparedStatement build() + { + Validate.notEmpty(parameters, "parameters must not be null"); + Validate.notBlank(query, "query must not be blank"); + Validate.notNull(connection, "connection must not be null"); + + LOGGER.info("Running query {}", this.query); + LOGGER.info("Parameters {}", this.parameters); + + try { + PreparedStatement preparedStatement = connection.prepareStatement(this.query); + + for (int i = 1; i <= parameters.size(); i++) { + preparedStatement.setString(i, parameters.get(i - 1)); + } + + return preparedStatement; + } + catch (SQLException sqlException) { + throw new RuntimeException(sqlException.getErrorCode() + ": " + sqlException.getMessage(), sqlException); + } + } +} diff --git a/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/mysql/MySqlCompositeHandler.java b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/mysql/MySqlCompositeHandler.java new file mode 100644 index 0000000000..bfbdcb9f33 --- /dev/null +++ b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/mysql/MySqlCompositeHandler.java @@ -0,0 +1,35 @@ +/*- + * #%L + * athena-jdbc + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.jdbc.mysql; + +import com.amazonaws.athena.connector.lambda.handlers.CompositeHandler; + +/** + * Boilerplate composite handler that allows us to use a single Lambda function for both + * Metadata and Data. In this case we just compose {@link MySqlMetadataHandler} and {@link MySqlRecordHandler}. + */ +public class MySqlCompositeHandler + extends CompositeHandler +{ + public MySqlCompositeHandler() + { + super(new MySqlMetadataHandler(), new MySqlRecordHandler()); + } +} diff --git a/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/mysql/MySqlMetadataHandler.java b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/mysql/MySqlMetadataHandler.java new file mode 100644 index 0000000000..3220cad210 --- /dev/null +++ b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/mysql/MySqlMetadataHandler.java @@ -0,0 +1,190 @@ +/*- + * #%L + * athena-jdbc + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.jdbc.mysql; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockWriter; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.spill.SpillLocation; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutRequest; +import com.amazonaws.connectors.athena.jdbc.connection.DatabaseConnectionConfig; +import com.amazonaws.connectors.athena.jdbc.connection.GenericJdbcConnectionFactory; +import com.amazonaws.connectors.athena.jdbc.connection.JdbcConnectionFactory; +import com.amazonaws.connectors.athena.jdbc.manager.JDBCUtil; +import com.amazonaws.connectors.athena.jdbc.manager.JdbcMetadataHandler; +import com.amazonaws.connectors.athena.jdbc.manager.PreparedStatementBuilder; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableMap; +import org.apache.arrow.vector.complex.reader.FieldReader; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Schema; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Handles metadata for MySQL. User must have access to `schemata`, `tables`, `columns`, `partitions` tables in + * information_schema. + */ +public class MySqlMetadataHandler + extends JdbcMetadataHandler +{ + static final Map JDBC_PROPERTIES = ImmutableMap.of("databaseTerm", "SCHEMA"); + static final String GET_PARTITIONS_QUERY = "SELECT DISTINCT partition_name FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME = ? AND TABLE_SCHEMA = ? " + + "AND partition_name IS NOT NULL"; + static final String BLOCK_PARTITION_COLUMN_NAME = "partition_name"; + static final String ALL_PARTITIONS = "*"; + static final String PARTITION_COLUMN_NAME = "partition_name"; + private static final Logger LOGGER = LoggerFactory.getLogger(MySqlMetadataHandler.class); + private static final int MAX_SPLITS_PER_REQUEST = 1000_000; + + public MySqlMetadataHandler() + { + this(JDBCUtil.getSingleDatabaseConfigFromEnv(JdbcConnectionFactory.DatabaseEngine.MYSQL)); + } + + /** + * Used by Mux. + */ + public MySqlMetadataHandler(final DatabaseConnectionConfig databaseConnectionConfig) + { + super(databaseConnectionConfig, new GenericJdbcConnectionFactory(databaseConnectionConfig, JDBC_PROPERTIES)); + } + + @VisibleForTesting + protected MySqlMetadataHandler(final DatabaseConnectionConfig databaseConnectionConfig, final AWSSecretsManager secretsManager, + AmazonAthena athena, final JdbcConnectionFactory jdbcConnectionFactory) + { + super(databaseConnectionConfig, secretsManager, athena, jdbcConnectionFactory); + } + + @Override + public Schema getPartitionSchema(final String catalogName) + { + SchemaBuilder schemaBuilder = SchemaBuilder.newBuilder() + .addField(BLOCK_PARTITION_COLUMN_NAME, Types.MinorType.VARCHAR.getType()); + return schemaBuilder.build(); + } + + @Override + public void getPartitions(final BlockWriter blockWriter, final GetTableLayoutRequest getTableLayoutRequest, QueryStatusChecker queryStatusChecker) + { + LOGGER.info("{}: Schema {}, table {}", getTableLayoutRequest.getQueryId(), getTableLayoutRequest.getTableName().getSchemaName(), + getTableLayoutRequest.getTableName().getTableName()); + try (Connection connection = getJdbcConnectionFactory().getConnection(getCredentialProvider())) { + final String escape = connection.getMetaData().getSearchStringEscape(); + + List parameters = Arrays.asList(getTableLayoutRequest.getTableName().getTableName(), getTableLayoutRequest.getTableName().getSchemaName()); + try (PreparedStatement preparedStatement = new PreparedStatementBuilder().withConnection(connection).withQuery(GET_PARTITIONS_QUERY).withParameters(parameters).build(); + ResultSet resultSet = preparedStatement.executeQuery()) { + // Return a single partition if no partitions defined + if (!resultSet.next()) { + blockWriter.writeRows((Block block, int rowNum) -> { + block.setValue(BLOCK_PARTITION_COLUMN_NAME, rowNum, ALL_PARTITIONS); + LOGGER.info("Adding partition {}", ALL_PARTITIONS); + //we wrote 1 row so we return 1 + return 1; + }); + } + else { + do { + final String partitionName = resultSet.getString(PARTITION_COLUMN_NAME); + + // 1. Returns all partitions of table, we are not supporting constraints push down to filter partitions. + // 2. This API is not paginated, we could use order by and limit clause with offsets here. + blockWriter.writeRows((Block block, int rowNum) -> { + block.setValue(BLOCK_PARTITION_COLUMN_NAME, rowNum, partitionName); + LOGGER.info("Adding partition {}", partitionName); + //we wrote 1 row so we return 1 + return 1; + }); + } + while (resultSet.next() && queryStatusChecker.isQueryRunning()); + } + } + } + catch (SQLException sqlException) { + throw new RuntimeException(sqlException.getErrorCode() + ": " + sqlException.getMessage(), sqlException); + } + } + + @Override + public GetSplitsResponse doGetSplits( + final BlockAllocator blockAllocator, final GetSplitsRequest getSplitsRequest) + { + LOGGER.info("{}: Catalog {}, table {}", getSplitsRequest.getQueryId(), getSplitsRequest.getTableName().getSchemaName(), getSplitsRequest.getTableName().getTableName()); + int partitionContd = decodeContinuationToken(getSplitsRequest); + Set splits = new HashSet<>(); + Block partitions = getSplitsRequest.getPartitions(); + + // TODO consider splitting further depending on #rows or data size. Could use Hash key for splitting if no partitions. + for (int curPartition = partitionContd; curPartition < partitions.getRowCount(); curPartition++) { + FieldReader locationReader = partitions.getFieldReader(BLOCK_PARTITION_COLUMN_NAME); + locationReader.setPosition(curPartition); + + SpillLocation spillLocation = makeSpillLocation(getSplitsRequest); + + LOGGER.info("{}: Input partition is {}", getSplitsRequest.getQueryId(), locationReader.readText()); + + Split.Builder splitBuilder = Split.newBuilder(spillLocation, makeEncryptionKey()) + .add(BLOCK_PARTITION_COLUMN_NAME, String.valueOf(locationReader.readText())); + + splits.add(splitBuilder.build()); + + if (splits.size() >= MAX_SPLITS_PER_REQUEST) { + //We exceeded the number of split we want to return in a single request, return and provide a continuation token. + return new GetSplitsResponse(getSplitsRequest.getCatalogName(), splits, encodeContinuationToken(curPartition)); + } + } + + return new GetSplitsResponse(getSplitsRequest.getCatalogName(), splits, null); + } + + private int decodeContinuationToken(GetSplitsRequest request) + { + if (request.hasContinuationToken()) { + return Integer.valueOf(request.getContinuationToken()); + } + + //No continuation token present + return 0; + } + + private String encodeContinuationToken(int partition) + { + return String.valueOf(partition); + } +} diff --git a/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/mysql/MySqlQueryStringBuilder.java b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/mysql/MySqlQueryStringBuilder.java new file mode 100644 index 0000000000..8d34e9b348 --- /dev/null +++ b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/mysql/MySqlQueryStringBuilder.java @@ -0,0 +1,55 @@ +/*- + * #%L + * athena-jdbc + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.jdbc.mysql; + +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.connectors.athena.jdbc.manager.JdbcSplitQueryBuilder; +import com.google.common.base.Strings; + +public class MySqlQueryStringBuilder + extends JdbcSplitQueryBuilder +{ + MySqlQueryStringBuilder(final String quoteCharacters) + { + super(quoteCharacters); + } + + @Override + protected String getFromClauseWithSplit(String catalog, String schema, String table, Split split) + { + StringBuilder tableName = new StringBuilder(); + if (!Strings.isNullOrEmpty(catalog)) { + tableName.append(quote(catalog)).append('.'); + } + if (!Strings.isNullOrEmpty(schema)) { + tableName.append(quote(schema)).append('.'); + } + tableName.append(quote(table)); + + String partitionName = split.getProperty(MySqlMetadataHandler.BLOCK_PARTITION_COLUMN_NAME); + + if (MySqlMetadataHandler.ALL_PARTITIONS.equals(partitionName)) { + // No partitions + return String.format(" FROM %s ", tableName); + } + + return String.format(" FROM %s PARTITION(%s) ", tableName, partitionName); + } +} diff --git a/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/mysql/MySqlRecordHandler.java b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/mysql/MySqlRecordHandler.java new file mode 100644 index 0000000000..d552543d9e --- /dev/null +++ b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/mysql/MySqlRecordHandler.java @@ -0,0 +1,89 @@ +/*- + * #%L + * athena-jdbc + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.jdbc.mysql; + +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.connectors.athena.jdbc.connection.DatabaseConnectionConfig; +import com.amazonaws.connectors.athena.jdbc.connection.GenericJdbcConnectionFactory; +import com.amazonaws.connectors.athena.jdbc.connection.JdbcConnectionFactory; +import com.amazonaws.connectors.athena.jdbc.manager.JDBCUtil; +import com.amazonaws.connectors.athena.jdbc.manager.JdbcRecordHandler; +import com.amazonaws.connectors.athena.jdbc.manager.JdbcSplitQueryBuilder; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.athena.AmazonAthenaClientBuilder; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.amazonaws.services.secretsmanager.AWSSecretsManagerClientBuilder; +import com.google.common.annotations.VisibleForTesting; +import org.apache.arrow.vector.types.pojo.Schema; +import org.apache.commons.lang3.Validate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; + +/** + * Data handler, user must have necessary permissions to read from necessary tables. + */ +public class MySqlRecordHandler + extends JdbcRecordHandler +{ + private static final Logger LOGGER = LoggerFactory.getLogger(MySqlRecordHandler.class); + + private static final String MYSQL_QUOTE_CHARACTER = "`"; + + private final JdbcSplitQueryBuilder jdbcSplitQueryBuilder; + + public MySqlRecordHandler() + { + this(JDBCUtil.getSingleDatabaseConfigFromEnv(JdbcConnectionFactory.DatabaseEngine.MYSQL)); + } + + public MySqlRecordHandler(final DatabaseConnectionConfig databaseConnectionConfig) + { + this(databaseConnectionConfig, AmazonS3ClientBuilder.defaultClient(), AWSSecretsManagerClientBuilder.defaultClient(), AmazonAthenaClientBuilder.defaultClient(), + new GenericJdbcConnectionFactory(databaseConnectionConfig, MySqlMetadataHandler.JDBC_PROPERTIES), new MySqlQueryStringBuilder(MYSQL_QUOTE_CHARACTER)); + } + + @VisibleForTesting + MySqlRecordHandler(final DatabaseConnectionConfig databaseConnectionConfig, final AmazonS3 amazonS3, final AWSSecretsManager secretsManager, + final AmazonAthena athena, final JdbcConnectionFactory jdbcConnectionFactory, final JdbcSplitQueryBuilder jdbcSplitQueryBuilder) + { + super(amazonS3, secretsManager, athena, databaseConnectionConfig, jdbcConnectionFactory); + this.jdbcSplitQueryBuilder = Validate.notNull(jdbcSplitQueryBuilder, "query builder must not be null"); + } + + @Override + public PreparedStatement buildSplitSql(Connection jdbcConnection, String catalogName, TableName tableName, Schema schema, Constraints constraints, Split split) + throws SQLException + { + PreparedStatement preparedStatement = jdbcSplitQueryBuilder.buildSql(jdbcConnection, null, tableName.getSchemaName(), tableName.getTableName(), schema, constraints, split); + + // Disable fetching all rows. + preparedStatement.setFetchSize(Integer.MIN_VALUE); + + return preparedStatement; + } +} diff --git a/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/postgresql/PostGreSqlCompositeHandler.java b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/postgresql/PostGreSqlCompositeHandler.java new file mode 100644 index 0000000000..157b33544e --- /dev/null +++ b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/postgresql/PostGreSqlCompositeHandler.java @@ -0,0 +1,37 @@ +/*- + * #%L + * athena-jdbc + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.jdbc.postgresql; + +import com.amazonaws.athena.connector.lambda.handlers.CompositeHandler; +import com.amazonaws.athena.connector.lambda.handlers.MetadataHandler; +import com.amazonaws.athena.connector.lambda.handlers.RecordHandler; + +/** + * Boilerplate composite handler that allows us to use a single Lambda function for both + * Metadata and Data. In this case we just compose {@link PostGreSqlMetadataHandler} and {@link PostGreSqlRecordHandler}. + */ +public class PostGreSqlCompositeHandler + extends CompositeHandler +{ + public PostGreSqlCompositeHandler(MetadataHandler metadataHandler, RecordHandler recordHandler) + { + super(new PostGreSqlMetadataHandler(), new PostGreSqlRecordHandler()); + } +} diff --git a/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/postgresql/PostGreSqlMetadataHandler.java b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/postgresql/PostGreSqlMetadataHandler.java new file mode 100644 index 0000000000..e87ef525a1 --- /dev/null +++ b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/postgresql/PostGreSqlMetadataHandler.java @@ -0,0 +1,190 @@ +/*- + * #%L + * athena-jdbc + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.jdbc.postgresql; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockWriter; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.spill.SpillLocation; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutRequest; +import com.amazonaws.connectors.athena.jdbc.connection.DatabaseConnectionConfig; +import com.amazonaws.connectors.athena.jdbc.connection.GenericJdbcConnectionFactory; +import com.amazonaws.connectors.athena.jdbc.connection.JdbcConnectionFactory; +import com.amazonaws.connectors.athena.jdbc.manager.JDBCUtil; +import com.amazonaws.connectors.athena.jdbc.manager.JdbcMetadataHandler; +import com.amazonaws.connectors.athena.jdbc.manager.PreparedStatementBuilder; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableMap; +import org.apache.arrow.vector.complex.reader.FieldReader; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Schema; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class PostGreSqlMetadataHandler + extends JdbcMetadataHandler +{ + static final Map JDBC_PROPERTIES = ImmutableMap.of("databaseTerm", "SCHEMA"); + static final String GET_PARTITIONS_QUERY = "SELECT nmsp_child.nspname AS child_schema, child.relname AS child FROM pg_inherits JOIN pg_class parent " + + "ON pg_inherits.inhparent = parent.oid JOIN pg_class child ON pg_inherits.inhrelid = child.oid JOIN pg_namespace nmsp_parent " + + "ON nmsp_parent.oid = parent.relnamespace JOIN pg_namespace nmsp_child ON nmsp_child.oid = child.relnamespace where nmsp_parent.nspname = ? " + + "AND parent.relname = ?"; + static final String BLOCK_PARTITION_COLUMN_NAME = "partition_name"; + static final String BLOCK_PARTITION_SCHEMA_COLUMN_NAME = "partition_schema_name"; + static final String ALL_PARTITIONS = "*"; + private static final Logger LOGGER = LoggerFactory.getLogger(PostGreSqlMetadataHandler.class); + private static final String PARTITION_SCHEMA_NAME = "child_schema"; + private static final String PARTITION_NAME = "child"; + private static final int MAX_SPLITS_PER_REQUEST = 1000_000; + + public PostGreSqlMetadataHandler() + { + this(JDBCUtil.getSingleDatabaseConfigFromEnv(JdbcConnectionFactory.DatabaseEngine.POSTGRES)); + } + + public PostGreSqlMetadataHandler(final DatabaseConnectionConfig databaseConnectionConfig) + { + super(databaseConnectionConfig, new GenericJdbcConnectionFactory(databaseConnectionConfig, JDBC_PROPERTIES)); + } + + @VisibleForTesting + protected PostGreSqlMetadataHandler(final DatabaseConnectionConfig databaseConnectionConfig, final AWSSecretsManager secretsManager, + final AmazonAthena athena, final JdbcConnectionFactory jdbcConnectionFactory) + { + super(databaseConnectionConfig, secretsManager, athena, jdbcConnectionFactory); + } + + @Override + public Schema getPartitionSchema(final String catalogName) + { + SchemaBuilder schemaBuilder = SchemaBuilder.newBuilder() + .addField(BLOCK_PARTITION_SCHEMA_COLUMN_NAME, Types.MinorType.VARCHAR.getType()) + .addField(BLOCK_PARTITION_COLUMN_NAME, Types.MinorType.VARCHAR.getType()); + return schemaBuilder.build(); + } + + @Override + public void getPartitions(final BlockWriter blockWriter, final GetTableLayoutRequest getTableLayoutRequest, QueryStatusChecker queryStatusChecker) + { + LOGGER.info("{}: Catalog {}, table {}", getTableLayoutRequest.getQueryId(), getTableLayoutRequest.getTableName().getSchemaName(), + getTableLayoutRequest.getTableName().getTableName()); + try (Connection connection = getJdbcConnectionFactory().getConnection(getCredentialProvider())) { + List parameters = Arrays.asList(getTableLayoutRequest.getTableName().getSchemaName(), + getTableLayoutRequest.getTableName().getTableName()); + try (PreparedStatement preparedStatement = new PreparedStatementBuilder().withConnection(connection).withQuery(GET_PARTITIONS_QUERY).withParameters(parameters).build(); + ResultSet resultSet = preparedStatement.executeQuery()) { + // Return a single partition if no partitions defined + if (!resultSet.next()) { + blockWriter.writeRows((Block block, int rowNum) -> { + block.setValue(BLOCK_PARTITION_SCHEMA_COLUMN_NAME, rowNum, ALL_PARTITIONS); + block.setValue(BLOCK_PARTITION_COLUMN_NAME, rowNum, ALL_PARTITIONS); + //we wrote 1 row so we return 1 + return 1; + }); + } + else { + do { + final String partitionSchemaName = resultSet.getString(PARTITION_SCHEMA_NAME); + final String partitionName = resultSet.getString(PARTITION_NAME); + + // 1. Returns all partitions of table, we are not supporting constraints push down to filter partitions. + // 2. This API is not paginated, we could use order by and limit clause with offsets here. + blockWriter.writeRows((Block block, int rowNum) -> { + block.setValue(BLOCK_PARTITION_SCHEMA_COLUMN_NAME, rowNum, partitionSchemaName); + block.setValue(BLOCK_PARTITION_COLUMN_NAME, rowNum, partitionName); + //we wrote 1 row so we return 1 + return 1; + }); + } + while (resultSet.next()); + } + } + } + catch (SQLException sqlException) { + throw new RuntimeException(sqlException.getErrorCode() + ": " + sqlException.getMessage(), sqlException); + } + } + + @Override + public GetSplitsResponse doGetSplits(BlockAllocator blockAllocator, GetSplitsRequest getSplitsRequest) + { + LOGGER.info("{}: Catalog {}, table {}", getSplitsRequest.getQueryId(), getSplitsRequest.getTableName().getSchemaName(), getSplitsRequest.getTableName().getTableName()); + int partitionContd = decodeContinuationToken(getSplitsRequest); + Set splits = new HashSet<>(); + Block partitions = getSplitsRequest.getPartitions(); + + // TODO consider splitting further depending on #rows or data size. Could use Hash key for splitting if no partitions. i/ATHENA-3979 + for (int curPartition = partitionContd; curPartition < partitions.getRowCount(); curPartition++) { + FieldReader partitionsSchemaFieldReader = partitions.getFieldReader(BLOCK_PARTITION_SCHEMA_COLUMN_NAME); + partitionsSchemaFieldReader.setPosition(curPartition); + FieldReader partitionsFieldReader = partitions.getFieldReader(BLOCK_PARTITION_COLUMN_NAME); + partitionsFieldReader.setPosition(curPartition); + + //Every split must have a unique location if we wish to spill to avoid failures + SpillLocation spillLocation = makeSpillLocation(getSplitsRequest); + + LOGGER.info("{}: Input partition is {}", getSplitsRequest.getQueryId(), String.valueOf(partitionsFieldReader.readText())); + Split.Builder splitBuilder = Split.newBuilder(spillLocation, makeEncryptionKey()) + .add(BLOCK_PARTITION_SCHEMA_COLUMN_NAME, String.valueOf(partitionsSchemaFieldReader.readText())) + .add(BLOCK_PARTITION_COLUMN_NAME, String.valueOf(partitionsFieldReader.readText())); + + splits.add(splitBuilder.build()); + + if (splits.size() >= MAX_SPLITS_PER_REQUEST) { + //We exceeded the number of split we want to return in a single request, return and provide a continuation token. + return new GetSplitsResponse(getSplitsRequest.getCatalogName(), splits, encodeContinuationToken(curPartition)); + } + } + + return new GetSplitsResponse(getSplitsRequest.getCatalogName(), splits, null); + } + + private int decodeContinuationToken(GetSplitsRequest request) + { + if (request.hasContinuationToken()) { + return Integer.valueOf(request.getContinuationToken()); + } + + //No continuation token present + return 0; + } + + private String encodeContinuationToken(int partition) + { + return String.valueOf(partition); + } +} diff --git a/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/postgresql/PostGreSqlQueryStringBuilder.java b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/postgresql/PostGreSqlQueryStringBuilder.java new file mode 100644 index 0000000000..57d124038b --- /dev/null +++ b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/postgresql/PostGreSqlQueryStringBuilder.java @@ -0,0 +1,56 @@ +/*- + * #%L + * athena-jdbc + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.jdbc.postgresql; + +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.connectors.athena.jdbc.manager.JdbcSplitQueryBuilder; +import com.google.common.base.Strings; + +public class PostGreSqlQueryStringBuilder + extends JdbcSplitQueryBuilder +{ + PostGreSqlQueryStringBuilder(final String quoteCharacters) + { + super(quoteCharacters); + } + + @Override + protected String getFromClauseWithSplit(String catalog, String schema, String table, Split split) + { + StringBuilder tableName = new StringBuilder(); + if (!Strings.isNullOrEmpty(catalog)) { + tableName.append(quote(catalog)).append('.'); + } + if (!Strings.isNullOrEmpty(schema)) { + tableName.append(quote(schema)).append('.'); + } + tableName.append(quote(table)); + + String partitionSchemaName = split.getProperty(PostGreSqlMetadataHandler.BLOCK_PARTITION_SCHEMA_COLUMN_NAME); + String partitionName = split.getProperty(PostGreSqlMetadataHandler.BLOCK_PARTITION_COLUMN_NAME); + + if (PostGreSqlMetadataHandler.ALL_PARTITIONS.equals(partitionName)) { + // No partitions + return String.format(" FROM %s ", tableName); + } + + return String.format(" FROM %s.%s ", quote(partitionSchemaName), quote(partitionName)); + } +} diff --git a/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/postgresql/PostGreSqlRecordHandler.java b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/postgresql/PostGreSqlRecordHandler.java new file mode 100644 index 0000000000..fc05e30764 --- /dev/null +++ b/athena-jdbc/src/main/java/com/amazonaws/connectors/athena/jdbc/postgresql/PostGreSqlRecordHandler.java @@ -0,0 +1,88 @@ +/*- + * #%L + * athena-jdbc + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.jdbc.postgresql; + +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.connectors.athena.jdbc.connection.DatabaseConnectionConfig; +import com.amazonaws.connectors.athena.jdbc.connection.GenericJdbcConnectionFactory; +import com.amazonaws.connectors.athena.jdbc.connection.JdbcConnectionFactory; +import com.amazonaws.connectors.athena.jdbc.manager.JDBCUtil; +import com.amazonaws.connectors.athena.jdbc.manager.JdbcRecordHandler; +import com.amazonaws.connectors.athena.jdbc.manager.JdbcSplitQueryBuilder; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.athena.AmazonAthenaClientBuilder; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.amazonaws.services.secretsmanager.AWSSecretsManagerClientBuilder; +import com.google.common.annotations.VisibleForTesting; +import org.apache.arrow.vector.types.pojo.Schema; +import org.apache.commons.lang3.Validate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; + +public class PostGreSqlRecordHandler + extends JdbcRecordHandler +{ + private static final Logger LOGGER = LoggerFactory.getLogger(PostGreSqlRecordHandler.class); + + private static final int FETCH_SIZE = 1000; + + private final JdbcSplitQueryBuilder jdbcSplitQueryBuilder; + + private static final String POSTGRES_QUOTE_CHARACTER = "\""; + + public PostGreSqlRecordHandler() + { + this(JDBCUtil.getSingleDatabaseConfigFromEnv(JdbcConnectionFactory.DatabaseEngine.POSTGRES)); + } + + public PostGreSqlRecordHandler(final DatabaseConnectionConfig databaseConnectionConfig) + { + this(databaseConnectionConfig, AmazonS3ClientBuilder.defaultClient(), AWSSecretsManagerClientBuilder.defaultClient(), AmazonAthenaClientBuilder.defaultClient(), + new GenericJdbcConnectionFactory(databaseConnectionConfig, PostGreSqlMetadataHandler.JDBC_PROPERTIES), new PostGreSqlQueryStringBuilder(POSTGRES_QUOTE_CHARACTER)); + } + + @VisibleForTesting + PostGreSqlRecordHandler(final DatabaseConnectionConfig databaseConnectionConfig, final AmazonS3 amazonS3, final AWSSecretsManager secretsManager, + final AmazonAthena athena, final JdbcConnectionFactory jdbcConnectionFactory, final JdbcSplitQueryBuilder jdbcSplitQueryBuilder) + { + super(amazonS3, secretsManager, athena, databaseConnectionConfig, jdbcConnectionFactory); + this.jdbcSplitQueryBuilder = Validate.notNull(jdbcSplitQueryBuilder, "query builder must not be null"); + } + + @Override + public PreparedStatement buildSplitSql(Connection jdbcConnection, String catalogName, TableName tableName, Schema schema, Constraints constraints, Split split) + throws SQLException + { + PreparedStatement preparedStatement = jdbcSplitQueryBuilder.buildSql(jdbcConnection, null, tableName.getSchemaName(), tableName.getTableName(), schema, constraints, split); + + // Disable fetching all rows. + preparedStatement.setFetchSize(FETCH_SIZE); + + return preparedStatement; + } +} diff --git a/athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/MultiplexingJdbcMetadataHandlerTest.java b/athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/MultiplexingJdbcMetadataHandlerTest.java new file mode 100644 index 0000000000..e5a4ca2d48 --- /dev/null +++ b/athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/MultiplexingJdbcMetadataHandlerTest.java @@ -0,0 +1,143 @@ +/*- + * #%L + * athena-jdbc + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.jdbc; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.amazonaws.athena.connector.lambda.data.BlockWriter; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesRequest; +import com.amazonaws.connectors.athena.jdbc.connection.DatabaseConnectionConfig; +import com.amazonaws.connectors.athena.jdbc.connection.JdbcConnectionFactory; +import com.amazonaws.connectors.athena.jdbc.manager.JdbcMetadataHandler; +import com.amazonaws.connectors.athena.jdbc.mysql.MySqlMetadataHandler; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import java.util.Collections; +import java.util.Map; + +public class MultiplexingJdbcMetadataHandlerTest +{ + private Map metadataHandlerMap; + private MySqlMetadataHandler mySqlMetadataHandler; + private JdbcMetadataHandler jdbcMetadataHandler; + private BlockAllocator allocator; + private AWSSecretsManager secretsManager; + private AmazonAthena athena; + private QueryStatusChecker queryStatusChecker; + private JdbcConnectionFactory jdbcConnectionFactory; + + @Before + public void setup() + { + //this.allocator = Mockito.mock(BlockAllocator.class); + this.allocator = new BlockAllocatorImpl(); + //Mockito.when(this.allocator.createBlock(Mockito.any(Schema.class))).thenReturn(Mockito.mock(Block.class)); + this.mySqlMetadataHandler = Mockito.mock(MySqlMetadataHandler.class); + this.metadataHandlerMap = Collections.singletonMap("mysql", this.mySqlMetadataHandler); + this.secretsManager = Mockito.mock(AWSSecretsManager.class); + this.athena = Mockito.mock(AmazonAthena.class); + this.queryStatusChecker = Mockito.mock(QueryStatusChecker.class); + this.jdbcConnectionFactory = Mockito.mock(JdbcConnectionFactory.class); + DatabaseConnectionConfig databaseConnectionConfig = new DatabaseConnectionConfig("testCatalog", JdbcConnectionFactory.DatabaseEngine.MYSQL, + "mysql://jdbc:mysql://hostname/${testSecret}", "testSecret"); + this.jdbcMetadataHandler = new MultiplexingJdbcMetadataHandler(this.secretsManager, this.athena, this.jdbcConnectionFactory, this.metadataHandlerMap, databaseConnectionConfig); + } + + @Test + public void doListSchemaNames() + { + ListSchemasRequest listSchemasRequest = Mockito.mock(ListSchemasRequest.class); + Mockito.when(listSchemasRequest.getCatalogName()).thenReturn("mysql"); + this.jdbcMetadataHandler.doListSchemaNames(this.allocator, listSchemasRequest); + Mockito.verify(this.mySqlMetadataHandler, Mockito.times(1)).doListSchemaNames(Mockito.eq(this.allocator), Mockito.eq(listSchemasRequest)); + } + + @Test + public void doListTables() + { + ListTablesRequest listTablesRequest = Mockito.mock(ListTablesRequest.class); + Mockito.when(listTablesRequest.getCatalogName()).thenReturn("mysql"); + this.jdbcMetadataHandler.doListTables(this.allocator, listTablesRequest); + Mockito.verify(this.mySqlMetadataHandler, Mockito.times(1)).doListTables(Mockito.eq(this.allocator), Mockito.eq(listTablesRequest)); + } + + @Test + public void doGetTable() + { + GetTableRequest getTableRequest = Mockito.mock(GetTableRequest.class); + Mockito.when(getTableRequest.getCatalogName()).thenReturn("mysql"); + this.jdbcMetadataHandler.doGetTable(this.allocator, getTableRequest); + Mockito.verify(this.mySqlMetadataHandler, Mockito.times(1)).doGetTable(Mockito.eq(this.allocator), Mockito.eq(getTableRequest)); + } + + @Test + public void doGetTableLayout() + throws Exception + { + GetTableLayoutRequest getTableLayoutRequest = Mockito.mock(GetTableLayoutRequest.class); + Mockito.when(getTableLayoutRequest.getTableName()).thenReturn(new TableName("testSchema", "testTable")); + Mockito.when(getTableLayoutRequest.getCatalogName()).thenReturn("mysql"); + this.jdbcMetadataHandler.doGetTableLayout(this.allocator, getTableLayoutRequest); + Mockito.verify(this.mySqlMetadataHandler, Mockito.times(1)).doGetTableLayout(Mockito.eq(this.allocator), Mockito.eq(getTableLayoutRequest)); + } + + @Test + public void getPartitionSchema() + { + this.jdbcMetadataHandler.getPartitionSchema("mysql"); + Mockito.verify(this.mySqlMetadataHandler, Mockito.times(1)).getPartitionSchema(Mockito.eq("mysql")); + } + + @Test(expected = RuntimeException.class) + public void getPartitionSchemaForUnsupportedCatalog() + { + this.jdbcMetadataHandler.getPartitionSchema("unsupportedCatalog"); + } + + + @Test + public void getPartitions() + throws Exception + { + GetTableLayoutRequest getTableLayoutRequest = Mockito.mock(GetTableLayoutRequest.class); + Mockito.when(getTableLayoutRequest.getCatalogName()).thenReturn("mysql"); + this.jdbcMetadataHandler.getPartitions(Mockito.mock(BlockWriter.class), getTableLayoutRequest, queryStatusChecker); + Mockito.verify(this.mySqlMetadataHandler, Mockito.times(1)).getPartitions(Mockito.any(BlockWriter.class), Mockito.eq(getTableLayoutRequest), Mockito.eq(queryStatusChecker)); + } + + @Test + public void doGetSplits() + { + GetSplitsRequest getSplitsRequest = Mockito.mock(GetSplitsRequest.class); + Mockito.when(getSplitsRequest.getCatalogName()).thenReturn("mysql"); + this.jdbcMetadataHandler.doGetSplits(this.allocator, getSplitsRequest); + Mockito.verify(this.mySqlMetadataHandler, Mockito.times(1)).doGetSplits(Mockito.eq(this.allocator), Mockito.eq(getSplitsRequest)); + } +} diff --git a/athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/MultiplexingJdbcRecordHandlerTest.java b/athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/MultiplexingJdbcRecordHandlerTest.java new file mode 100644 index 0000000000..d1a9e3ae02 --- /dev/null +++ b/athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/MultiplexingJdbcRecordHandlerTest.java @@ -0,0 +1,104 @@ +/*- + * #%L + * athena-jdbc + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.jdbc; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.BlockSpiller; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.connectors.athena.jdbc.connection.DatabaseConnectionConfig; +import com.amazonaws.connectors.athena.jdbc.connection.JdbcConnectionFactory; +import com.amazonaws.connectors.athena.jdbc.manager.JdbcRecordHandler; +import com.amazonaws.connectors.athena.jdbc.mysql.MySqlRecordHandler; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import org.apache.arrow.vector.types.pojo.Schema; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Collections; +import java.util.Map; + +public class MultiplexingJdbcRecordHandlerTest +{ + private Map recordHandlerMap; + private MySqlRecordHandler mySqlRecordHandler; + private JdbcRecordHandler jdbcRecordHandler; + private AmazonS3 amazonS3; + private AWSSecretsManager secretsManager; + private AmazonAthena athena; + private QueryStatusChecker queryStatusChecker; + private JdbcConnectionFactory jdbcConnectionFactory; + + @Before + public void setup() + { + this.mySqlRecordHandler = Mockito.mock(MySqlRecordHandler.class); + this.recordHandlerMap = Collections.singletonMap("mysql", this.mySqlRecordHandler); + this.amazonS3 = Mockito.mock(AmazonS3.class); + this.secretsManager = Mockito.mock(AWSSecretsManager.class); + this.athena = Mockito.mock(AmazonAthena.class); + this.queryStatusChecker = Mockito.mock(QueryStatusChecker.class); + this.jdbcConnectionFactory = Mockito.mock(JdbcConnectionFactory.class); + DatabaseConnectionConfig databaseConnectionConfig = new DatabaseConnectionConfig("testCatalog", JdbcConnectionFactory.DatabaseEngine.MYSQL, + "mysql://jdbc:mysql://hostname/${testSecret}", "testSecret"); + this.jdbcRecordHandler = new MultiplexingJdbcRecordHandler(this.amazonS3, this.secretsManager, this.athena, this.jdbcConnectionFactory, databaseConnectionConfig, this.recordHandlerMap); + } + + @Test + public void readWithConstraint() + { + BlockSpiller blockSpiller = Mockito.mock(BlockSpiller.class); + ReadRecordsRequest readRecordsRequest = Mockito.mock(ReadRecordsRequest.class); + Mockito.when(readRecordsRequest.getCatalogName()).thenReturn("mysql"); + this.jdbcRecordHandler.readWithConstraint(blockSpiller, readRecordsRequest, queryStatusChecker); + Mockito.verify(this.mySqlRecordHandler, Mockito.times(1)).readWithConstraint(Mockito.eq(blockSpiller), Mockito.eq(readRecordsRequest), Mockito.eq(queryStatusChecker)); + } + + @Test(expected = RuntimeException.class) + public void readWithConstraintWithUnsupportedCatalog() + { + BlockSpiller blockSpiller = Mockito.mock(BlockSpiller.class); + ReadRecordsRequest readRecordsRequest = Mockito.mock(ReadRecordsRequest.class); + Mockito.when(readRecordsRequest.getCatalogName()).thenReturn("unsupportedCatalog"); + this.jdbcRecordHandler.readWithConstraint(blockSpiller, readRecordsRequest, queryStatusChecker); + } + + @Test + public void buildSplitSql() + throws SQLException + { + ReadRecordsRequest readRecordsRequest = Mockito.mock(ReadRecordsRequest.class); + Mockito.when(readRecordsRequest.getCatalogName()).thenReturn("mysql"); + Connection jdbcConnection = Mockito.mock(Connection.class); + TableName tableName = new TableName("testSchema", "tableName"); + Schema schema = Mockito.mock(Schema.class); + Constraints constraints = Mockito.mock(Constraints.class); + Split split = Mockito.mock(Split.class); + this.jdbcRecordHandler.buildSplitSql(jdbcConnection, "mysql", tableName, schema, constraints, split); + Mockito.verify(this.mySqlRecordHandler, Mockito.times(1)).buildSplitSql(Mockito.eq(jdbcConnection), Mockito.eq("mysql"), Mockito.eq(tableName), Mockito.eq(schema), Mockito.eq(constraints), Mockito.eq(split)); + } +} diff --git a/athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/TestBase.java b/athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/TestBase.java new file mode 100644 index 0000000000..9168e0a8b6 --- /dev/null +++ b/athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/TestBase.java @@ -0,0 +1,91 @@ +/*- + * #%L + * athena-jdbc + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.jdbc; + +import org.mockito.Mockito; +import org.mockito.stubbing.Answer; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicInteger; + +public class TestBase +{ + protected ResultSet mockResultSet(String[] columnNames, int[] columnTypes, Object[][] rows, AtomicInteger rowNumber) + throws SQLException + { + ResultSet resultSet = Mockito.mock(ResultSet.class, Mockito.RETURNS_DEEP_STUBS); + + Mockito.when(resultSet.next()).thenAnswer( + (Answer) invocation -> { + if (rows.length <= 0 || rows[0].length <= 0) { + return false; + } + + return rowNumber.getAndIncrement() + 1 < rows.length; + }); + + Mockito.when(resultSet.getInt(Mockito.any())).thenAnswer((Answer) invocation -> { + Object argument = invocation.getArguments()[0]; + + if (argument instanceof Integer) { + int colIndex = (Integer) argument; + return (Integer) rows[rowNumber.get()][colIndex] - 1; + } + else if (argument instanceof String) { + int colIndex = Arrays.asList(columnNames).indexOf(argument); + return (Integer) rows[rowNumber.get()][colIndex]; + } + else { + throw new RuntimeException("Unexpected argument type " + argument.getClass()); + } + }); + + Mockito.when(resultSet.getString(Mockito.any())).thenAnswer((Answer) invocation -> { + Object argument = invocation.getArguments()[0]; + if (argument instanceof Integer) { + int colIndex = (Integer) argument - 1; + return String.valueOf(rows[rowNumber.get()][colIndex]); + } + else if (argument instanceof String) { + int colIndex = Arrays.asList(columnNames).indexOf(argument); + return String.valueOf(rows[rowNumber.get()][colIndex]); + } + else { + throw new RuntimeException("Unexpected argument type " + argument.getClass()); + } + }); + + if (columnTypes != null) { + Mockito.when(resultSet.getMetaData().getColumnCount()).thenReturn(columnNames.length); + Mockito.when(resultSet.getMetaData().getColumnDisplaySize(Mockito.anyInt())).thenReturn(10); + Mockito.when(resultSet.getMetaData().getColumnType(Mockito.anyInt())).thenAnswer((Answer) invocation -> columnTypes[(Integer) invocation.getArguments()[0] - 1]); + } + + return resultSet; + } + + protected ResultSet mockResultSet(String[] columnNames, Object[][] rows, AtomicInteger rowNumber) + throws SQLException + { + return this.mockResultSet(columnNames, null, rows, rowNumber); + } +} diff --git a/athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/connection/DatabaseConnectionConfigBuilderTest.java b/athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/connection/DatabaseConnectionConfigBuilderTest.java new file mode 100644 index 0000000000..747cf061a6 --- /dev/null +++ b/athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/connection/DatabaseConnectionConfigBuilderTest.java @@ -0,0 +1,72 @@ +/*- + * #%L + * athena-jdbc + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.jdbc.connection; + +import com.google.common.collect.ImmutableMap; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class DatabaseConnectionConfigBuilderTest +{ + private static final String CONNECTION_STRING1 = "mysql://jdbc:mysql://hostname/${testSecret}"; + private static final String CONNECTION_STRING2 = "postgres://jdbc:postgresql://hostname/user=testUser&password=testPassword"; + + @Test + public void build() + { + DatabaseConnectionConfig expectedDatabase1 = new DatabaseConnectionConfig("testCatalog1", JdbcConnectionFactory.DatabaseEngine.MYSQL, + "jdbc:mysql://hostname/${testSecret}", "testSecret"); + DatabaseConnectionConfig expectedDatabase2 = new DatabaseConnectionConfig("testCatalog2", JdbcConnectionFactory.DatabaseEngine.POSTGRES, + "jdbc:postgresql://hostname/user=testUser&password=testPassword"); + DatabaseConnectionConfig defaultConnection = new DatabaseConnectionConfig("default", JdbcConnectionFactory.DatabaseEngine.POSTGRES, + "jdbc:postgresql://hostname/user=testUser&password=testPassword"); + + List databaseConnectionConfigs = new DatabaseConnectionConfigBuilder() + .properties(ImmutableMap.of( + "default", CONNECTION_STRING2, + "testCatalog1_connection_string", CONNECTION_STRING1, + "testCatalog2_connection_string", CONNECTION_STRING2)) + .build(); + + Assert.assertEquals(Arrays.asList(defaultConnection, expectedDatabase1, expectedDatabase2), databaseConnectionConfigs); + } + + @Test(expected = RuntimeException.class) + public void buildInvalidConnectionString() + { + new DatabaseConnectionConfigBuilder().properties(Collections.singletonMap("default", "malformedUrl")).build(); + } + + @Test(expected = RuntimeException.class) + public void buildWithNoDefault() + { + new DatabaseConnectionConfigBuilder().properties(Collections.singletonMap("testDb_connection_string", CONNECTION_STRING1)).build(); + } + + @Test(expected = RuntimeException.class) + public void buildMalformedConnectionString() + { + new DatabaseConnectionConfigBuilder().properties(Collections.singletonMap("testDb_connection_string", null)).build(); + } +} diff --git a/athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/connection/JdbcCredentialProviderTest.java b/athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/connection/JdbcCredentialProviderTest.java new file mode 100644 index 0000000000..ba5edb1745 --- /dev/null +++ b/athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/connection/JdbcCredentialProviderTest.java @@ -0,0 +1,53 @@ +/*- + * #%L + * athena-jdbc + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.jdbc.connection; + +import org.junit.Assert; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class JdbcCredentialProviderTest +{ + + @Test + public void getStaticCredential() + { + JdbcCredential expectedCredential = new JdbcCredential("testUser", "testPassword"); + JdbcCredentialProvider jdbcCredentialProvider = new StaticJdbcCredentialProvider(expectedCredential); + + Assert.assertEquals(expectedCredential, jdbcCredentialProvider.getCredential()); + } + + @Test + public void getRdsSecretsCredential() + { + JdbcCredential expectedCredential = new JdbcCredential("testUser", "testPassword"); + JdbcCredentialProvider jdbcCredentialProvider = new RdsSecretsCredentialProvider("{\"username\": \"testUser\", \"password\": \"testPassword\"}"); + + Assert.assertEquals(expectedCredential, jdbcCredentialProvider.getCredential()); + } + + @Test(expected = RuntimeException.class) + public void getRdsSecretsCredentialIOException() + { + new RdsSecretsCredentialProvider(""); + } +} diff --git a/athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/manager/JDBCUtilTest.java b/athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/manager/JDBCUtilTest.java new file mode 100644 index 0000000000..478aab1f3f --- /dev/null +++ b/athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/manager/JDBCUtilTest.java @@ -0,0 +1,101 @@ +/*- + * #%L + * athena-jdbc + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.jdbc.manager; + +import com.amazonaws.connectors.athena.jdbc.mysql.MySqlMetadataHandler; +import com.amazonaws.connectors.athena.jdbc.mysql.MySqlRecordHandler; +import com.amazonaws.connectors.athena.jdbc.postgresql.PostGreSqlMetadataHandler; +import com.amazonaws.connectors.athena.jdbc.postgresql.PostGreSqlRecordHandler; +import com.google.common.collect.ImmutableMap; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Collections; +import java.util.Map; + +public class JDBCUtilTest +{ + private static final int PORT = 1111; + private static final String CONNECTION_STRING1 = "mysql://jdbc:mysql://hostname/${testSecret}"; + private static final String CONNECTION_STRING2 = "postgres://jdbc:postgresql://hostname/user=testUser&password=testPassword"; + + + @Test + public void createJdbcMetadataHandlerMap() + { + Map catalogs = JDBCUtil.createJdbcMetadataHandlerMap(ImmutableMap.builder() + .put("testCatalog1_connection_string", CONNECTION_STRING1) + .put("testCatalog2_connection_string", CONNECTION_STRING2) + .put("default", CONNECTION_STRING2) + .put("AWS_LAMBDA_FUNCTION_NAME", "functionName") + .build()); + + Assert.assertEquals(4, catalogs.size()); + Assert.assertEquals(catalogs.get("testCatalog1").getClass(), MySqlMetadataHandler.class); + Assert.assertEquals(catalogs.get("testCatalog2").getClass(), PostGreSqlMetadataHandler.class); + Assert.assertEquals(catalogs.get("lambda:functionName").getClass(), PostGreSqlMetadataHandler.class); + } + + @Test(expected = RuntimeException.class) + public void createJdbcMetadataHandlerEmptyConnectionStrings() + { + JDBCUtil.createJdbcMetadataHandlerMap(Collections.emptyMap()); + } + + @Test(expected = RuntimeException.class) + public void createJdbcMetadataHandlerNoDefault() + { + JDBCUtil.createJdbcMetadataHandlerMap(ImmutableMap.builder() + .put("testCatalog1_connection_string", CONNECTION_STRING1) + .put("testCatalog2_connection_string", CONNECTION_STRING2) + .build()); + } + + + @Test + public void createJdbcRecordHandlerMap() + { + Map catalogs = JDBCUtil.createJdbcRecordHandlerMap(ImmutableMap.builder() + .put("testCatalog1_connection_string", CONNECTION_STRING1) + .put("testCatalog2_connection_string", CONNECTION_STRING2) + .put("default", CONNECTION_STRING2) + .put("AWS_LAMBDA_FUNCTION_NAME", "functionName") + .build()); + + Assert.assertEquals(catalogs.get("testCatalog1").getClass(), MySqlRecordHandler.class); + Assert.assertEquals(catalogs.get("testCatalog2").getClass(), PostGreSqlRecordHandler.class); + Assert.assertEquals(catalogs.get("lambda:functionName").getClass(), PostGreSqlRecordHandler.class); + } + + @Test(expected = RuntimeException.class) + public void createJdbcRecordHandlerMapEmptyConnectionStrings() + { + JDBCUtil.createJdbcRecordHandlerMap(Collections.emptyMap()); + } + + @Test(expected = RuntimeException.class) + public void createJdbcRecordHandlerMapNoDefault() + { + JDBCUtil.createJdbcRecordHandlerMap(ImmutableMap.builder() + .put("testCatalog1_connection_string", CONNECTION_STRING1) + .put("testCatalog2_connection_string", CONNECTION_STRING2) + .build()); + } +} diff --git a/athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/manager/JdbcArrowTypeConverterTest.java b/athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/manager/JdbcArrowTypeConverterTest.java new file mode 100644 index 0000000000..0dab186f1a --- /dev/null +++ b/athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/manager/JdbcArrowTypeConverterTest.java @@ -0,0 +1,55 @@ +/*- + * #%L + * athena-jdbc + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.jdbc.manager; + +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.junit.Assert; +import org.junit.Test; + +public class JdbcArrowTypeConverterTest +{ + @Test + public void toArrowType() + { + Assert.assertEquals(Types.MinorType.BIT.getType(), JdbcArrowTypeConverter.toArrowType(java.sql.Types.BIT, 0, 0)); + Assert.assertEquals(Types.MinorType.BIT.getType(), JdbcArrowTypeConverter.toArrowType(java.sql.Types.BOOLEAN, 0, 0)); + Assert.assertEquals(Types.MinorType.TINYINT.getType(), JdbcArrowTypeConverter.toArrowType(java.sql.Types.TINYINT, 0, 0)); + Assert.assertEquals(Types.MinorType.SMALLINT.getType(), JdbcArrowTypeConverter.toArrowType(java.sql.Types.SMALLINT, 0, 0)); + Assert.assertEquals(Types.MinorType.INT.getType(), JdbcArrowTypeConverter.toArrowType(java.sql.Types.INTEGER, 0, 0)); + Assert.assertEquals(Types.MinorType.BIGINT.getType(), JdbcArrowTypeConverter.toArrowType(java.sql.Types.BIGINT, 0, 0)); + Assert.assertEquals(Types.MinorType.FLOAT4.getType(), JdbcArrowTypeConverter.toArrowType(java.sql.Types.REAL, 0, 0)); + Assert.assertEquals(Types.MinorType.FLOAT4.getType(), JdbcArrowTypeConverter.toArrowType(java.sql.Types.FLOAT, 0, 0)); + Assert.assertEquals(Types.MinorType.FLOAT8.getType(), JdbcArrowTypeConverter.toArrowType(java.sql.Types.DOUBLE, 0, 0)); + Assert.assertEquals(new ArrowType.Decimal(5, 3), JdbcArrowTypeConverter.toArrowType(java.sql.Types.DECIMAL, 5, 3)); + Assert.assertEquals(Types.MinorType.VARCHAR.getType(), JdbcArrowTypeConverter.toArrowType(java.sql.Types.CHAR, 0, 0)); + Assert.assertEquals(Types.MinorType.VARCHAR.getType(), JdbcArrowTypeConverter.toArrowType(java.sql.Types.NCHAR, 0, 0)); + Assert.assertEquals(Types.MinorType.VARCHAR.getType(), JdbcArrowTypeConverter.toArrowType(java.sql.Types.VARCHAR, 0, 0)); + Assert.assertEquals(Types.MinorType.VARCHAR.getType(), JdbcArrowTypeConverter.toArrowType(java.sql.Types.NVARCHAR, 0, 0)); + Assert.assertEquals(Types.MinorType.VARCHAR.getType(), JdbcArrowTypeConverter.toArrowType(java.sql.Types.LONGVARCHAR, 0, 0)); + Assert.assertEquals(Types.MinorType.VARCHAR.getType(), JdbcArrowTypeConverter.toArrowType(java.sql.Types.LONGNVARCHAR, 0, 0)); + Assert.assertEquals(Types.MinorType.VARBINARY.getType(), JdbcArrowTypeConverter.toArrowType(java.sql.Types.BINARY, 0, 0)); + Assert.assertEquals(Types.MinorType.VARBINARY.getType(), JdbcArrowTypeConverter.toArrowType(java.sql.Types.VARBINARY, 0, 0)); + Assert.assertEquals(Types.MinorType.VARBINARY.getType(), JdbcArrowTypeConverter.toArrowType(java.sql.Types.LONGVARBINARY, 0, 0)); + Assert.assertEquals(Types.MinorType.DATEMILLI.getType(), JdbcArrowTypeConverter.toArrowType(java.sql.Types.DATE, 0, 0)); + Assert.assertEquals(Types.MinorType.TIMEMILLI.getType(), JdbcArrowTypeConverter.toArrowType(java.sql.Types.TIME, 0, 0)); + Assert.assertEquals(Types.MinorType.TIMESTAMPMILLI.getType(), JdbcArrowTypeConverter.toArrowType(java.sql.Types.TIMESTAMP, 0, 0)); + } +} diff --git a/athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/manager/JdbcMetadataHandlerTest.java b/athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/manager/JdbcMetadataHandlerTest.java new file mode 100644 index 0000000000..e9467862ca --- /dev/null +++ b/athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/manager/JdbcMetadataHandlerTest.java @@ -0,0 +1,227 @@ +/*- + * #%L + * athena-jdbc + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.jdbc.manager; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockWriter; +import com.amazonaws.athena.connector.lambda.data.FieldBuilder; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesResponse; +import com.amazonaws.athena.connector.lambda.security.FederatedIdentity; +import com.amazonaws.connectors.athena.jdbc.TestBase; +import com.amazonaws.connectors.athena.jdbc.connection.DatabaseConnectionConfig; +import com.amazonaws.connectors.athena.jdbc.connection.JdbcConnectionFactory; +import com.amazonaws.connectors.athena.jdbc.connection.JdbcCredentialProvider; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.amazonaws.services.secretsmanager.model.GetSecretValueRequest; +import com.amazonaws.services.secretsmanager.model.GetSecretValueResult; +import org.apache.arrow.vector.types.pojo.Schema; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; +import java.util.concurrent.atomic.AtomicInteger; + +public class JdbcMetadataHandlerTest + extends TestBase +{ + private static final Schema PARTITION_SCHEMA = SchemaBuilder.newBuilder().addField("testPartitionCol", org.apache.arrow.vector.types.Types.MinorType.VARCHAR.getType()).build(); + + private JdbcMetadataHandler jdbcMetadataHandler; + private JdbcConnectionFactory jdbcConnectionFactory; + private FederatedIdentity federatedIdentity; + private Connection connection; + private BlockAllocator blockAllocator; + private AWSSecretsManager secretsManager; + private AmazonAthena athena; + + @Before + public void setup() + { + this.jdbcConnectionFactory = Mockito.mock(JdbcConnectionFactory.class); + this.connection = Mockito.mock(Connection.class, Mockito.RETURNS_DEEP_STUBS); + Mockito.when(this.jdbcConnectionFactory.getConnection(Mockito.any(JdbcCredentialProvider.class))).thenReturn(this.connection); + this.secretsManager = Mockito.mock(AWSSecretsManager.class); + this.athena = Mockito.mock(AmazonAthena.class); + Mockito.when(this.secretsManager.getSecretValue(Mockito.eq(new GetSecretValueRequest().withSecretId("testSecret")))).thenReturn(new GetSecretValueResult().withSecretString("{\"username\": \"testUser\", \"password\": \"testPassword\"}")); + DatabaseConnectionConfig databaseConnectionConfig = new DatabaseConnectionConfig("testCatalog", JdbcConnectionFactory.DatabaseEngine.MYSQL, + "mysql://jdbc:mysql://hostname/${testSecret}", "testSecret"); + this.jdbcMetadataHandler = new JdbcMetadataHandler(databaseConnectionConfig, this.secretsManager, this.athena, jdbcConnectionFactory) + { + @Override + public Schema getPartitionSchema(final String catalogName) + { + return PARTITION_SCHEMA; + } + + @Override + public void getPartitions(final BlockWriter blockWriter, final GetTableLayoutRequest getTableLayoutRequest, QueryStatusChecker queryStatusChecker) + { + } + + @Override + public GetSplitsResponse doGetSplits(BlockAllocator blockAllocator, GetSplitsRequest getSplitsRequest) + { + return null; + } + }; + this.federatedIdentity = Mockito.mock(FederatedIdentity.class); + this.blockAllocator = Mockito.mock(BlockAllocator.class); + } + + @Test + public void getJdbcConnectionFactory() + { + Assert.assertEquals(this.jdbcConnectionFactory, this.jdbcMetadataHandler.getJdbcConnectionFactory()); + } + + @Test + public void doListSchemaNames() + throws SQLException + { + String[] schema = {"TABLE_SCHEM"}; + Object[][] values = {{"testDB"}, {"testdb2"}, {"information_schema"}}; + String[] expected = {"testDB", "testdb2"}; + AtomicInteger rowNumber = new AtomicInteger(-1); + ResultSet resultSet = mockResultSet(schema, values, rowNumber); + Mockito.when(connection.getMetaData().getSchemas()).thenReturn(resultSet); + ListSchemasResponse listSchemasResponse = this.jdbcMetadataHandler.doListSchemaNames(this.blockAllocator, new ListSchemasRequest(this.federatedIdentity, "testQueryId", "testCatalog")); + Assert.assertArrayEquals(expected, listSchemasResponse.getSchemas().toArray()); + } + + @Test + public void doListTables() + throws SQLException + { + String[] schema = {"TABLE_SCHEM", "TABLE_NAME"}; + Object[][] values = {{"testSchema", "testTable"}, {"testSchema", "testtable2"}}; + TableName[] expected = {new TableName("testSchema", "testTable"), new TableName("testSchema", "testtable2")}; + AtomicInteger rowNumber = new AtomicInteger(-1); + ResultSet resultSet = mockResultSet(schema, values, rowNumber); + + Mockito.when(connection.getMetaData().getTables("testCatalog", "testSchema", null, new String[] {"TABLE", "VIEW"})).thenReturn(resultSet); + Mockito.when(connection.getCatalog()).thenReturn("testCatalog"); + ListTablesResponse listTablesResponse = this.jdbcMetadataHandler.doListTables( + this.blockAllocator, new ListTablesRequest(this.federatedIdentity, "testQueryId", "testCatalog", "testSchema")); + Assert.assertArrayEquals(expected, listTablesResponse.getTables().toArray()); + } + + @Test + public void doListTablesEscaped() + throws SQLException + { + String[] schema = {"TABLE_SCHEM", "TABLE_NAME"}; + Object[][] values = {{"test_Schema", "testTable"}, {"test_Schema", "testtable2"}}; + TableName[] expected = {new TableName("test_Schema", "testTable"), new TableName("test_Schema", "testtable2")}; + AtomicInteger rowNumber = new AtomicInteger(-1); + ResultSet resultSet = mockResultSet(schema, values, rowNumber); + Mockito.when(connection.getMetaData().getTables("testCatalog", "test\\_Schema", null, new String[] {"TABLE", "VIEW"})).thenReturn(resultSet); + Mockito.when(connection.getCatalog()).thenReturn("testCatalog"); + Mockito.when(connection.getMetaData().getSearchStringEscape()).thenReturn("\\"); + ListTablesResponse listTablesResponse = this.jdbcMetadataHandler.doListTables( + this.blockAllocator, new ListTablesRequest(this.federatedIdentity, "testQueryId", "testCatalog", "test_Schema")); + Assert.assertArrayEquals(expected, listTablesResponse.getTables().toArray()); + } + + @Test(expected = IllegalArgumentException.class) + public void doListTablesEscapedException() + throws SQLException + { + Mockito.when(connection.getMetaData().getSearchStringEscape()).thenReturn("_"); + this.jdbcMetadataHandler.doListTables(this.blockAllocator, new ListTablesRequest(this.federatedIdentity, "testQueryId", "testCatalog", "test_Schema")); + } + + @Test + public void doGetTable() + throws SQLException + { + String[] schema = {"DATA_TYPE", "COLUMN_SIZE", "COLUMN_NAME", "DECIMAL_DIGITS", "NUM_PREC_RADIX"}; + Object[][] values = {{Types.INTEGER, 12, "testCol1", 0, 0}, {Types.VARCHAR, 25, "testCol2", 0, 0}}; + AtomicInteger rowNumber = new AtomicInteger(-1); + ResultSet resultSet = mockResultSet(schema, values, rowNumber); + + SchemaBuilder expectedSchemaBuilder = SchemaBuilder.newBuilder(); + expectedSchemaBuilder.addField(FieldBuilder.newBuilder("testCol1", org.apache.arrow.vector.types.Types.MinorType.INT.getType()).build()); + expectedSchemaBuilder.addField(FieldBuilder.newBuilder("testCol2", org.apache.arrow.vector.types.Types.MinorType.VARCHAR.getType()).build()); + PARTITION_SCHEMA.getFields().forEach(expectedSchemaBuilder::addField); + Schema expected = expectedSchemaBuilder.build(); + + TableName inputTableName = new TableName("testSchema", "testTable"); + Mockito.when(connection.getMetaData().getColumns("testCatalog", inputTableName.getSchemaName(), inputTableName.getTableName(), null)).thenReturn(resultSet); + Mockito.when(connection.getCatalog()).thenReturn("testCatalog"); + + GetTableResponse getTableResponse = this.jdbcMetadataHandler.doGetTable( + this.blockAllocator, new GetTableRequest(this.federatedIdentity, "testQueryId", "testCatalog", inputTableName)); + + Assert.assertEquals(expected, getTableResponse.getSchema()); + Assert.assertEquals(inputTableName, getTableResponse.getTableName()); + Assert.assertEquals("testCatalog", getTableResponse.getCatalogName()); + } + + @Test(expected = RuntimeException.class) + public void doGetTableNoColumns() + { + TableName inputTableName = new TableName("testSchema", "testTable"); + + this.jdbcMetadataHandler.doGetTable(this.blockAllocator, new GetTableRequest(this.federatedIdentity, "testQueryId", "testCatalog", inputTableName)); + } + + @Test(expected = RuntimeException.class) + public void doGetTableSQLException() + throws SQLException + { + TableName inputTableName = new TableName("testSchema", "testTable"); + Mockito.when(this.connection.getMetaData().getColumns(Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) + .thenThrow(new SQLException()); + this.jdbcMetadataHandler.doGetTable(this.blockAllocator, new GetTableRequest(this.federatedIdentity, "testQueryId", "testCatalog", inputTableName)); + } + + @Test(expected = RuntimeException.class) + public void doListSchemaNamesSQLException() + throws SQLException + { + Mockito.when(this.connection.getMetaData().getSchemas()).thenThrow(new SQLException()); + this.jdbcMetadataHandler.doListSchemaNames(this.blockAllocator, new ListSchemasRequest(this.federatedIdentity, "testQueryId", "testCatalog")); + } + + @Test(expected = RuntimeException.class) + public void doListTablesSQLException() + throws SQLException + { + Mockito.when(this.connection.getMetaData().getTables(Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.any())).thenThrow(new SQLException()); + this.jdbcMetadataHandler.doListTables(this.blockAllocator, new ListTablesRequest(this.federatedIdentity, "testQueryId", "testCatalog", "testSchema")); + } +} diff --git a/athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/manager/JdbcRecordHandlerTest.java b/athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/manager/JdbcRecordHandlerTest.java new file mode 100644 index 0000000000..aa84ab044d --- /dev/null +++ b/athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/manager/JdbcRecordHandlerTest.java @@ -0,0 +1,151 @@ +/*- + * #%L + * athena-jdbc + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.jdbc.manager; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.amazonaws.athena.connector.lambda.data.BlockSpiller; +import com.amazonaws.athena.connector.lambda.data.FieldBuilder; +import com.amazonaws.athena.connector.lambda.data.S3BlockSpiller; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.data.SpillConfig; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.ConstraintEvaluator; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.domain.spill.S3SpillLocation; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.athena.connector.lambda.security.FederatedIdentity; +import com.amazonaws.connectors.athena.jdbc.TestBase; +import com.amazonaws.connectors.athena.jdbc.connection.DatabaseConnectionConfig; +import com.amazonaws.connectors.athena.jdbc.connection.JdbcConnectionFactory; +import com.amazonaws.connectors.athena.jdbc.connection.JdbcCredentialProvider; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.PutObjectResult; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.amazonaws.services.secretsmanager.model.GetSecretValueRequest; +import com.amazonaws.services.secretsmanager.model.GetSecretValueResult; +import org.apache.arrow.vector.types.pojo.Schema; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.mockito.stubbing.Answer; + +import java.io.ByteArrayInputStream; +import java.nio.charset.StandardCharsets; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; +import java.util.concurrent.atomic.AtomicInteger; + +public class JdbcRecordHandlerTest + extends TestBase +{ + + private JdbcRecordHandler jdbcRecordHandler; + private Connection connection; + private JdbcConnectionFactory jdbcConnectionFactory; + private AmazonS3 amazonS3; + private AWSSecretsManager secretsManager; + private AmazonAthena athena; + private QueryStatusChecker queryStatusChecker; + private FederatedIdentity federatedIdentity; + private PreparedStatement preparedStatement; + + @Before + public void setup() + throws SQLException + { + this.connection = Mockito.mock(Connection.class, Mockito.RETURNS_DEEP_STUBS); + this.jdbcConnectionFactory = Mockito.mock(JdbcConnectionFactory.class); + Mockito.when(this.jdbcConnectionFactory.getConnection(Mockito.any(JdbcCredentialProvider.class))).thenReturn(this.connection); + this.amazonS3 = Mockito.mock(AmazonS3.class); + this.secretsManager = Mockito.mock(AWSSecretsManager.class); + this.athena = Mockito.mock(AmazonAthena.class); + this.queryStatusChecker = Mockito.mock(QueryStatusChecker.class); + Mockito.when(this.secretsManager.getSecretValue(Mockito.eq(new GetSecretValueRequest().withSecretId("testSecret")))).thenReturn(new GetSecretValueResult().withSecretString("{\"username\": \"testUser\", \"password\": \"testPassword\"}")); + this.preparedStatement = Mockito.mock(PreparedStatement.class); + Mockito.when(this.connection.prepareStatement("someSql")).thenReturn(this.preparedStatement); + DatabaseConnectionConfig databaseConnectionConfig = new DatabaseConnectionConfig("testCatalog", JdbcConnectionFactory.DatabaseEngine.MYSQL, + "mysql://jdbc:mysql://hostname/${testSecret}", "testSecret"); + this.jdbcRecordHandler = new JdbcRecordHandler(this.amazonS3, this.secretsManager, this.athena, databaseConnectionConfig, this.jdbcConnectionFactory) + { + @Override + public PreparedStatement buildSplitSql(Connection jdbcConnection, String catalogName, TableName tableName, Schema schema, Constraints constraints, Split split) + throws SQLException + { + return jdbcConnection.prepareStatement("someSql"); + } + }; + this.federatedIdentity = Mockito.mock(FederatedIdentity.class); + } + + @Test + public void readWithConstraint() + throws SQLException + { + ConstraintEvaluator constraintEvaluator = Mockito.mock(ConstraintEvaluator.class); + Mockito.when(constraintEvaluator.apply(Mockito.anyString(), Mockito.any())).thenReturn(true); + + TableName inputTableName = new TableName("testSchema", "testTable"); + SchemaBuilder expectedSchemaBuilder = SchemaBuilder.newBuilder(); + expectedSchemaBuilder.addField(FieldBuilder.newBuilder("testCol1", org.apache.arrow.vector.types.Types.MinorType.INT.getType()).build()); + expectedSchemaBuilder.addField(FieldBuilder.newBuilder("testCol2", org.apache.arrow.vector.types.Types.MinorType.VARCHAR.getType()).build()); + expectedSchemaBuilder.addField(FieldBuilder.newBuilder("testPartitionCol", org.apache.arrow.vector.types.Types.MinorType.VARCHAR.getType()).build()); + Schema fieldSchema = expectedSchemaBuilder.build(); + + BlockAllocator allocator = new BlockAllocatorImpl(); + S3SpillLocation s3SpillLocation = S3SpillLocation.newBuilder().withIsDirectory(true).build(); + + Split.Builder splitBuilder = Split.newBuilder(s3SpillLocation, null) + .add("testPartitionCol", String.valueOf("testPartitionValue")); + + Constraints constraints = Mockito.mock(Constraints.class, Mockito.RETURNS_DEEP_STUBS); + + String[] schema = {"testCol1", "testCol2"}; + int[] columnTypes = {Types.INTEGER, Types.VARCHAR}; + Object[][] values = {{1, "testVal1"}, {2, "testVal2"}}; + AtomicInteger rowNumber = new AtomicInteger(-1); + ResultSet resultSet = mockResultSet(schema, columnTypes, values, rowNumber); + Mockito.when(this.preparedStatement.executeQuery()).thenReturn(resultSet); + + SpillConfig spillConfig = Mockito.mock(SpillConfig.class); + Mockito.when(spillConfig.getSpillLocation()).thenReturn(s3SpillLocation); + BlockSpiller s3Spiller = new S3BlockSpiller(this.amazonS3, spillConfig, allocator, fieldSchema, constraintEvaluator); + ReadRecordsRequest readRecordsRequest = new ReadRecordsRequest(this.federatedIdentity, "testCatalog", "testQueryId", inputTableName, fieldSchema, splitBuilder.build(), constraints, 1024, 1024); + + Mockito.when(amazonS3.putObject(Mockito.anyString(), Mockito.anyString(), Mockito.any(), Mockito.any())).thenAnswer((Answer) invocation -> { + ByteArrayInputStream byteArrayInputStream = (ByteArrayInputStream) invocation.getArguments()[2]; + int n = byteArrayInputStream.available(); + byte[] bytes = new byte[n]; + byteArrayInputStream.read(bytes, 0, n); + String data = new String(bytes, StandardCharsets.UTF_8); + Assert.assertTrue(data.contains("testVal1") || data.contains("testVal2") || data.contains("testPartitionValue")); + return new PutObjectResult(); + }); + + this.jdbcRecordHandler.readWithConstraint(s3Spiller, readRecordsRequest, queryStatusChecker); + } +} diff --git a/athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/mysql/MySqlMetadataHandlerTest.java b/athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/mysql/MySqlMetadataHandlerTest.java new file mode 100644 index 0000000000..378e47d958 --- /dev/null +++ b/athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/mysql/MySqlMetadataHandlerTest.java @@ -0,0 +1,273 @@ +/*- + * #%L + * athena-jdbc + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.jdbc.mysql; + +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.amazonaws.athena.connector.lambda.data.BlockUtils; +import com.amazonaws.athena.connector.lambda.data.FieldBuilder; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutResponse; +import com.amazonaws.athena.connector.lambda.security.FederatedIdentity; +import com.amazonaws.connectors.athena.jdbc.TestBase; +import com.amazonaws.connectors.athena.jdbc.connection.DatabaseConnectionConfig; +import com.amazonaws.connectors.athena.jdbc.connection.JdbcConnectionFactory; +import com.amazonaws.connectors.athena.jdbc.connection.JdbcCredentialProvider; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.amazonaws.services.secretsmanager.model.GetSecretValueRequest; +import com.amazonaws.services.secretsmanager.model.GetSecretValueResult; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.arrow.vector.types.pojo.Schema; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +public class MySqlMetadataHandlerTest + extends TestBase +{ + private DatabaseConnectionConfig databaseConnectionConfig = new DatabaseConnectionConfig("testCatalog", JdbcConnectionFactory.DatabaseEngine.MYSQL, + "mysql://jdbc:mysql://hostname/user=A&password=B"); + private MySqlMetadataHandler mySqlMetadataHandler; + private JdbcConnectionFactory jdbcConnectionFactory; + private Connection connection; + private FederatedIdentity federatedIdentity; + private AWSSecretsManager secretsManager; + private AmazonAthena athena; + + @Before + public void setup() + { + this.jdbcConnectionFactory = Mockito.mock(JdbcConnectionFactory.class); + this.connection = Mockito.mock(Connection.class, Mockito.RETURNS_DEEP_STUBS); + Mockito.when(this.jdbcConnectionFactory.getConnection(Mockito.any(JdbcCredentialProvider.class))).thenReturn(this.connection); + this.secretsManager = Mockito.mock(AWSSecretsManager.class); + this.athena = Mockito.mock(AmazonAthena.class); + Mockito.when(this.secretsManager.getSecretValue(Mockito.eq(new GetSecretValueRequest().withSecretId("testSecret")))).thenReturn(new GetSecretValueResult().withSecretString("{\"username\": \"testUser\", \"password\": \"testPassword\"}")); + this.mySqlMetadataHandler = new MySqlMetadataHandler(databaseConnectionConfig, this.secretsManager, this.athena, this.jdbcConnectionFactory); + this.federatedIdentity = Mockito.mock(FederatedIdentity.class); + } + + @Test + public void getPartitionSchema() + { + Assert.assertEquals(SchemaBuilder.newBuilder() + .addField(MySqlMetadataHandler.BLOCK_PARTITION_COLUMN_NAME, org.apache.arrow.vector.types.Types.MinorType.VARCHAR.getType()).build(), + this.mySqlMetadataHandler.getPartitionSchema("testCatalogName")); + } + + @Test + public void doGetTableLayout() + throws Exception + { + BlockAllocator blockAllocator = new BlockAllocatorImpl(); + Constraints constraints = Mockito.mock(Constraints.class); + TableName tableName = new TableName("testSchema", "testTable"); + Schema partitionSchema = this.mySqlMetadataHandler.getPartitionSchema("testCatalogName"); + Set partitionCols = partitionSchema.getFields().stream().map(Field::getName).collect(Collectors.toSet()); + GetTableLayoutRequest getTableLayoutRequest = new GetTableLayoutRequest(this.federatedIdentity, "testQueryId", "testCatalogName", tableName, constraints, partitionSchema, partitionCols); + + PreparedStatement preparedStatement = Mockito.mock(PreparedStatement.class); + Mockito.when(this.connection.prepareStatement(MySqlMetadataHandler.GET_PARTITIONS_QUERY)).thenReturn(preparedStatement); + + String[] columns = {"partition_name"}; + int[] types = {Types.VARCHAR}; + Object[][] values = {{"p0"}, {"p1"}}; + ResultSet resultSet = mockResultSet(columns, types, values, new AtomicInteger(-1)); + Mockito.when(preparedStatement.executeQuery()).thenReturn(resultSet); + + Mockito.when(this.connection.getMetaData().getSearchStringEscape()).thenReturn(null); + + GetTableLayoutResponse getTableLayoutResponse = this.mySqlMetadataHandler.doGetTableLayout(blockAllocator, getTableLayoutRequest); + + Assert.assertEquals(values.length, getTableLayoutResponse.getPartitions().getRowCount()); + + List expectedValues = new ArrayList<>(); + for (int i = 0; i < getTableLayoutResponse.getPartitions().getRowCount(); i++) { + expectedValues.add(BlockUtils.rowToString(getTableLayoutResponse.getPartitions(), i)); + } + Assert.assertEquals(expectedValues, Arrays.asList("[partition_name : p0]", "[partition_name : p1]")); + + SchemaBuilder expectedSchemaBuilder = SchemaBuilder.newBuilder(); + expectedSchemaBuilder.addField(FieldBuilder.newBuilder(MySqlMetadataHandler.BLOCK_PARTITION_COLUMN_NAME, org.apache.arrow.vector.types.Types.MinorType.VARCHAR.getType()).build()); + Schema expectedSchema = expectedSchemaBuilder.build(); + Assert.assertEquals(expectedSchema, getTableLayoutResponse.getPartitions().getSchema()); + Assert.assertEquals(tableName, getTableLayoutResponse.getTableName()); + + Mockito.verify(preparedStatement, Mockito.times(1)).setString(1, tableName.getTableName()); + Mockito.verify(preparedStatement, Mockito.times(1)).setString(2, tableName.getSchemaName()); + } + + @Test + public void doGetTableLayoutWithNoPartitions() + throws Exception + { + BlockAllocator blockAllocator = new BlockAllocatorImpl(); + Constraints constraints = Mockito.mock(Constraints.class); + TableName tableName = new TableName("testSchema", "testTable"); + Schema partitionSchema = this.mySqlMetadataHandler.getPartitionSchema("testCatalogName"); + Set partitionCols = partitionSchema.getFields().stream().map(Field::getName).collect(Collectors.toSet()); + GetTableLayoutRequest getTableLayoutRequest = new GetTableLayoutRequest(this.federatedIdentity, "testQueryId", "testCatalogName", tableName, constraints, partitionSchema, partitionCols); + + PreparedStatement preparedStatement = Mockito.mock(PreparedStatement.class); + Mockito.when(this.connection.prepareStatement(MySqlMetadataHandler.GET_PARTITIONS_QUERY)).thenReturn(preparedStatement); + + String[] columns = {"partition_name"}; + int[] types = {Types.VARCHAR}; + Object[][] values = {{}}; + ResultSet resultSet = mockResultSet(columns, types, values, new AtomicInteger(-1)); + Mockito.when(preparedStatement.executeQuery()).thenReturn(resultSet); + + Mockito.when(this.connection.getMetaData().getSearchStringEscape()).thenReturn(null); + + GetTableLayoutResponse getTableLayoutResponse = this.mySqlMetadataHandler.doGetTableLayout(blockAllocator, getTableLayoutRequest); + + Assert.assertEquals(values.length, getTableLayoutResponse.getPartitions().getRowCount()); + + List expectedValues = new ArrayList<>(); + for (int i = 0; i < getTableLayoutResponse.getPartitions().getRowCount(); i++) { + expectedValues.add(BlockUtils.rowToString(getTableLayoutResponse.getPartitions(), i)); + } + Assert.assertEquals(expectedValues, Collections.singletonList("[partition_name : *]")); + + SchemaBuilder expectedSchemaBuilder = SchemaBuilder.newBuilder(); + expectedSchemaBuilder.addField(FieldBuilder.newBuilder(MySqlMetadataHandler.BLOCK_PARTITION_COLUMN_NAME, org.apache.arrow.vector.types.Types.MinorType.VARCHAR.getType()).build()); + Schema expectedSchema = expectedSchemaBuilder.build(); + Assert.assertEquals(expectedSchema, getTableLayoutResponse.getPartitions().getSchema()); + Assert.assertEquals(tableName, getTableLayoutResponse.getTableName()); + + Mockito.verify(preparedStatement, Mockito.times(1)).setString(1, tableName.getTableName()); + Mockito.verify(preparedStatement, Mockito.times(1)).setString(2, tableName.getSchemaName()); + } + + @Test(expected = RuntimeException.class) + public void doGetTableLayoutWithSQLException() + throws Exception + { + Constraints constraints = Mockito.mock(Constraints.class); + TableName tableName = new TableName("testSchema", "testTable"); + Schema partitionSchema = this.mySqlMetadataHandler.getPartitionSchema("testCatalogName"); + Set partitionCols = partitionSchema.getFields().stream().map(Field::getName).collect(Collectors.toSet()); + GetTableLayoutRequest getTableLayoutRequest = new GetTableLayoutRequest(this.federatedIdentity, "testQueryId", "testCatalogName", tableName, constraints, partitionSchema, partitionCols); + + Connection connection = Mockito.mock(Connection.class, Mockito.RETURNS_DEEP_STUBS); + JdbcConnectionFactory jdbcConnectionFactory = Mockito.mock(JdbcConnectionFactory.class); + Mockito.when(jdbcConnectionFactory.getConnection(Mockito.any(JdbcCredentialProvider.class))).thenReturn(connection); + Mockito.when(connection.getMetaData().getSearchStringEscape()).thenThrow(new SQLException()); + MySqlMetadataHandler mySqlMetadataHandler = new MySqlMetadataHandler(databaseConnectionConfig, this.secretsManager, this.athena, jdbcConnectionFactory); + + mySqlMetadataHandler.doGetTableLayout(Mockito.mock(BlockAllocator.class), getTableLayoutRequest); + } + + @Test + public void doGetSplits() + throws Exception + { + BlockAllocator blockAllocator = new BlockAllocatorImpl(); + Constraints constraints = Mockito.mock(Constraints.class); + TableName tableName = new TableName("testSchema", "testTable"); + + PreparedStatement preparedStatement = Mockito.mock(PreparedStatement.class); + Mockito.when(this.connection.prepareStatement(MySqlMetadataHandler.GET_PARTITIONS_QUERY)).thenReturn(preparedStatement); + + String[] columns = {MySqlMetadataHandler.PARTITION_COLUMN_NAME}; + int[] types = {Types.VARCHAR}; + Object[][] values = {{"p0"}, {"p1"}}; + ResultSet resultSet = mockResultSet(columns, types, values, new AtomicInteger(-1)); + Mockito.when(preparedStatement.executeQuery()).thenReturn(resultSet); + + Mockito.when(this.connection.getMetaData().getSearchStringEscape()).thenReturn(null); + + Schema partitionSchema = this.mySqlMetadataHandler.getPartitionSchema("testCatalogName"); + Set partitionCols = partitionSchema.getFields().stream().map(Field::getName).collect(Collectors.toSet()); + GetTableLayoutRequest getTableLayoutRequest = new GetTableLayoutRequest(this.federatedIdentity, "testQueryId", "testCatalogName", tableName, constraints, partitionSchema, partitionCols); + + GetTableLayoutResponse getTableLayoutResponse = this.mySqlMetadataHandler.doGetTableLayout(blockAllocator, getTableLayoutRequest); + + BlockAllocator splitBlockAllocator = new BlockAllocatorImpl(); + GetSplitsRequest getSplitsRequest = new GetSplitsRequest(this.federatedIdentity, "testQueryId", "testCatalogName", tableName, getTableLayoutResponse.getPartitions(), new ArrayList<>(partitionCols), constraints, null); + GetSplitsResponse getSplitsResponse = this.mySqlMetadataHandler.doGetSplits(splitBlockAllocator, getSplitsRequest); + + Set> expectedSplits = new HashSet<>(); + expectedSplits.add(Collections.singletonMap(MySqlMetadataHandler.BLOCK_PARTITION_COLUMN_NAME, "p0")); + expectedSplits.add(Collections.singletonMap(MySqlMetadataHandler.BLOCK_PARTITION_COLUMN_NAME, "p1")); + Assert.assertEquals(expectedSplits.size(), getSplitsResponse.getSplits().size()); + Set> actualSplits = getSplitsResponse.getSplits().stream().map(Split::getProperties).collect(Collectors.toSet()); + Assert.assertEquals(expectedSplits, actualSplits); + } + + @Test + public void doGetSplitsContinuation() + throws Exception + { + BlockAllocator blockAllocator = new BlockAllocatorImpl(); + Constraints constraints = Mockito.mock(Constraints.class); + TableName tableName = new TableName("testSchema", "testTable"); + Schema partitionSchema = this.mySqlMetadataHandler.getPartitionSchema("testCatalogName"); + Set partitionCols = partitionSchema.getFields().stream().map(Field::getName).collect(Collectors.toSet()); + GetTableLayoutRequest getTableLayoutRequest = new GetTableLayoutRequest(this.federatedIdentity, "testQueryId", "testCatalogName", tableName, constraints, partitionSchema, partitionCols); + + PreparedStatement preparedStatement = Mockito.mock(PreparedStatement.class); + Mockito.when(this.connection.prepareStatement(MySqlMetadataHandler.GET_PARTITIONS_QUERY)).thenReturn(preparedStatement); + + String[] columns = {"partition_name"}; + int[] types = {Types.VARCHAR}; + Object[][] values = {{"p0"}, {"p1"}}; + ResultSet resultSet = mockResultSet(columns, types, values, new AtomicInteger(-1)); + final String expectedQuery = String.format(MySqlMetadataHandler.GET_PARTITIONS_QUERY, tableName.getTableName(), tableName.getSchemaName()); + Mockito.when(preparedStatement.executeQuery()).thenReturn(resultSet); + + Mockito.when(this.connection.getMetaData().getSearchStringEscape()).thenReturn(null); + + GetTableLayoutResponse getTableLayoutResponse = this.mySqlMetadataHandler.doGetTableLayout(blockAllocator, getTableLayoutRequest); + + BlockAllocator splitBlockAllocator = new BlockAllocatorImpl(); + GetSplitsRequest getSplitsRequest = new GetSplitsRequest(this.federatedIdentity, "testQueryId", "testCatalogName", tableName, getTableLayoutResponse.getPartitions(), new ArrayList<>(partitionCols), constraints, "1"); + GetSplitsResponse getSplitsResponse = this.mySqlMetadataHandler.doGetSplits(splitBlockAllocator, getSplitsRequest); + + Set> expectedSplits = new HashSet<>(); + expectedSplits.add(Collections.singletonMap("partition_name", "p1")); + Assert.assertEquals(expectedSplits.size(), getSplitsResponse.getSplits().size()); + Set> actualSplits = getSplitsResponse.getSplits().stream().map(Split::getProperties).collect(Collectors.toSet()); + Assert.assertEquals(expectedSplits, actualSplits); + } +} diff --git a/athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/mysql/MySqlRecordHandlerTest.java b/athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/mysql/MySqlRecordHandlerTest.java new file mode 100644 index 0000000000..882bdf772d --- /dev/null +++ b/athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/mysql/MySqlRecordHandlerTest.java @@ -0,0 +1,170 @@ +/*- + * #%L + * athena-jdbc + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.jdbc.mysql; + +import com.amazonaws.athena.connector.lambda.data.FieldBuilder; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.domain.predicate.Marker; +import com.amazonaws.athena.connector.lambda.domain.predicate.Range; +import com.amazonaws.athena.connector.lambda.domain.predicate.SortedRangeSet; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import com.amazonaws.connectors.athena.jdbc.connection.DatabaseConnectionConfig; +import com.amazonaws.connectors.athena.jdbc.connection.JdbcConnectionFactory; +import com.amazonaws.connectors.athena.jdbc.connection.JdbcCredentialProvider; +import com.amazonaws.connectors.athena.jdbc.manager.JdbcMetadataHandler; +import com.amazonaws.connectors.athena.jdbc.manager.JdbcSplitQueryBuilder; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Schema; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.Collections; + +public class MySqlRecordHandlerTest +{ + private MySqlRecordHandler mySqlRecordHandler; + private Connection connection; + private JdbcConnectionFactory jdbcConnectionFactory; + private JdbcSplitQueryBuilder jdbcSplitQueryBuilder; + private AmazonS3 amazonS3; + private AWSSecretsManager secretsManager; + private AmazonAthena athena; + + @Before + public void setup() + { + this.amazonS3 = Mockito.mock(AmazonS3.class); + this.secretsManager = Mockito.mock(AWSSecretsManager.class); + this.athena = Mockito.mock(AmazonAthena.class); + this.connection = Mockito.mock(Connection.class); + this.jdbcConnectionFactory = Mockito.mock(JdbcConnectionFactory.class); + Mockito.when(this.jdbcConnectionFactory.getConnection(Mockito.mock(JdbcCredentialProvider.class))).thenReturn(this.connection); + jdbcSplitQueryBuilder = new MySqlQueryStringBuilder("`"); + final DatabaseConnectionConfig databaseConnectionConfig = new DatabaseConnectionConfig("testCatalog", JdbcConnectionFactory.DatabaseEngine.MYSQL, + "mysql://jdbc:mysql://hostname/user=A&password=B"); + + this.mySqlRecordHandler = new MySqlRecordHandler(databaseConnectionConfig, amazonS3, secretsManager, athena, jdbcConnectionFactory, jdbcSplitQueryBuilder); + } + + @Test + public void buildSplitSql() + throws SQLException + { + TableName tableName = new TableName("testSchema", "testTable"); + + SchemaBuilder schemaBuilder = SchemaBuilder.newBuilder(); + schemaBuilder.addField(FieldBuilder.newBuilder("testCol1", Types.MinorType.INT.getType()).build()); + schemaBuilder.addField(FieldBuilder.newBuilder("testCol2", Types.MinorType.VARCHAR.getType()).build()); + schemaBuilder.addField(FieldBuilder.newBuilder("testCol3", Types.MinorType.BIGINT.getType()).build()); + schemaBuilder.addField(FieldBuilder.newBuilder("testCol4", Types.MinorType.FLOAT4.getType()).build()); + schemaBuilder.addField(FieldBuilder.newBuilder("testCol5", Types.MinorType.SMALLINT.getType()).build()); + schemaBuilder.addField(FieldBuilder.newBuilder("testCol6", Types.MinorType.TINYINT.getType()).build()); + schemaBuilder.addField(FieldBuilder.newBuilder("testCol7", Types.MinorType.FLOAT8.getType()).build()); + schemaBuilder.addField(FieldBuilder.newBuilder("testCol8", Types.MinorType.BIT.getType()).build()); + schemaBuilder.addField(FieldBuilder.newBuilder("partition_name", Types.MinorType.VARCHAR.getType()).build()); + Schema schema = schemaBuilder.build(); + + Split split = Mockito.mock(Split.class); + Mockito.when(split.getProperties()).thenReturn(Collections.singletonMap("partition_name", "p0")); + Mockito.when(split.getProperty(Mockito.eq("partition_name"))).thenReturn("p0"); + + Range range1a = Mockito.mock(Range.class, Mockito.RETURNS_DEEP_STUBS); + Mockito.when(range1a.isSingleValue()).thenReturn(true); + Mockito.when(range1a.getLow().getValue()).thenReturn(1); + Range range1b = Mockito.mock(Range.class, Mockito.RETURNS_DEEP_STUBS); + Mockito.when(range1b.isSingleValue()).thenReturn(true); + Mockito.when(range1b.getLow().getValue()).thenReturn(2); + ValueSet valueSet1 = Mockito.mock(SortedRangeSet.class, Mockito.RETURNS_DEEP_STUBS); + Mockito.when(valueSet1.getRanges().getOrderedRanges()).thenReturn(ImmutableList.of(range1a, range1b)); + + ValueSet valueSet2 = getRangeSet(Marker.Bound.EXACTLY, "1", Marker.Bound.BELOW, "10"); + ValueSet valueSet3 = getRangeSet(Marker.Bound.ABOVE, 2L, Marker.Bound.EXACTLY, 20L); + ValueSet valueSet4 = getSingleValueSet(1.1F); + ValueSet valueSet5 = getSingleValueSet(1); + ValueSet valueSet6 = getSingleValueSet(0); + ValueSet valueSet7 = getSingleValueSet(1.2d); + ValueSet valueSet8 = getSingleValueSet(true); + + Constraints constraints = Mockito.mock(Constraints.class); + Mockito.when(constraints.getSummary()).thenReturn(new ImmutableMap.Builder() + .put("testCol1", valueSet1) + .put("testCol2", valueSet2) + .put("testCol3", valueSet3) + .put("testCol4", valueSet4) + .put("testCol5", valueSet5) + .put("testCol6", valueSet6) + .put("testCol7", valueSet7) + .put("testCol8", valueSet8) + .build()); + + String expectedSql = "SELECT `testCol1`, `testCol2`, `testCol3`, `testCol4`, `testCol5`, `testCol6`, `testCol7`, `testCol8` FROM `testSchema`.`testTable` PARTITION(p0) WHERE (`testCol1` IN (?,?)) AND ((`testCol2` >= ? AND `testCol2` < ?)) AND ((`testCol3` > ? AND `testCol3` <= ?)) AND (`testCol4` = ?) AND (`testCol5` = ?) AND (`testCol6` = ?) AND (`testCol7` = ?) AND (`testCol8` = ?)"; + PreparedStatement expectedPreparedStatement = Mockito.mock(PreparedStatement.class); + Mockito.when(this.connection.prepareStatement(Mockito.eq(expectedSql))).thenReturn(expectedPreparedStatement); + + PreparedStatement preparedStatement = this.mySqlRecordHandler.buildSplitSql(this.connection, "testCatalogName", tableName, schema, constraints, split); + + Assert.assertEquals(expectedPreparedStatement, preparedStatement); + Mockito.verify(preparedStatement, Mockito.times(1)).setInt(1, 1); + Mockito.verify(preparedStatement, Mockito.times(1)).setInt(2, 2); + Mockito.verify(preparedStatement, Mockito.times(1)).setString(3, "1"); + Mockito.verify(preparedStatement, Mockito.times(1)).setString(4, "10"); + Mockito.verify(preparedStatement, Mockito.times(1)).setLong(5, 2L); + Mockito.verify(preparedStatement, Mockito.times(1)).setLong(6, 20L); + Mockito.verify(preparedStatement, Mockito.times(1)).setFloat(7, 1.1F); + Mockito.verify(preparedStatement, Mockito.times(1)).setShort(8, (short) 1); + Mockito.verify(preparedStatement, Mockito.times(1)).setByte(9, (byte) 0); + Mockito.verify(preparedStatement, Mockito.times(1)).setDouble(10, 1.2d); + Mockito.verify(preparedStatement, Mockito.times(1)).setBoolean(11, true); + } + + private ValueSet getSingleValueSet(Object value) { + Range range = Mockito.mock(Range.class, Mockito.RETURNS_DEEP_STUBS); + Mockito.when(range.isSingleValue()).thenReturn(true); + Mockito.when(range.getLow().getValue()).thenReturn(value); + ValueSet valueSet = Mockito.mock(SortedRangeSet.class, Mockito.RETURNS_DEEP_STUBS); + Mockito.when(valueSet.getRanges().getOrderedRanges()).thenReturn(Collections.singletonList(range)); + return valueSet; + } + + private ValueSet getRangeSet(Marker.Bound lowerBound, Object lowerValue, Marker.Bound upperBound, Object upperValue) { + Range range = Mockito.mock(Range.class, Mockito.RETURNS_DEEP_STUBS); + Mockito.when(range.isSingleValue()).thenReturn(false); + Mockito.when(range.getLow().getBound()).thenReturn(lowerBound); + Mockito.when(range.getLow().getValue()).thenReturn(lowerValue); + Mockito.when(range.getHigh().getBound()).thenReturn(upperBound); + Mockito.when(range.getHigh().getValue()).thenReturn(upperValue); + ValueSet valueSet = Mockito.mock(SortedRangeSet.class, Mockito.RETURNS_DEEP_STUBS); + Mockito.when(valueSet.getRanges().getOrderedRanges()).thenReturn(Collections.singletonList(range)); + return valueSet; + } +} diff --git a/athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/postgresql/PostGreSqlMetadataHandlerTest.java b/athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/postgresql/PostGreSqlMetadataHandlerTest.java new file mode 100644 index 0000000000..df917ed5af --- /dev/null +++ b/athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/postgresql/PostGreSqlMetadataHandlerTest.java @@ -0,0 +1,278 @@ +/*- + * #%L + * athena-jdbc + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.jdbc.postgresql; + +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.amazonaws.athena.connector.lambda.data.BlockUtils; +import com.amazonaws.athena.connector.lambda.data.FieldBuilder; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutResponse; +import com.amazonaws.athena.connector.lambda.security.FederatedIdentity; +import com.amazonaws.connectors.athena.jdbc.TestBase; +import com.amazonaws.connectors.athena.jdbc.connection.DatabaseConnectionConfig; +import com.amazonaws.connectors.athena.jdbc.connection.JdbcConnectionFactory; +import com.amazonaws.connectors.athena.jdbc.connection.JdbcCredentialProvider; +import com.amazonaws.connectors.athena.jdbc.manager.JdbcMetadataHandler; +import com.amazonaws.connectors.athena.jdbc.mysql.MySqlMetadataHandler; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.amazonaws.services.secretsmanager.model.GetSecretValueRequest; +import com.amazonaws.services.secretsmanager.model.GetSecretValueResult; +import com.google.common.collect.ImmutableMap; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.arrow.vector.types.pojo.Schema; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +public class PostGreSqlMetadataHandlerTest + extends TestBase +{ + private DatabaseConnectionConfig databaseConnectionConfig = new DatabaseConnectionConfig("testCatalog", JdbcConnectionFactory.DatabaseEngine.MYSQL, + "mysql://jdbc:mysql://hostname/user=A&password=B"); + private PostGreSqlMetadataHandler postGreSqlMetadataHandler; + private JdbcConnectionFactory jdbcConnectionFactory; + private Connection connection; + private FederatedIdentity federatedIdentity; + private AWSSecretsManager secretsManager; + private AmazonAthena athena; + + @Before + public void setup() + { + this.jdbcConnectionFactory = Mockito.mock(JdbcConnectionFactory.class); + this.connection = Mockito.mock(Connection.class, Mockito.RETURNS_DEEP_STUBS); + Mockito.when(this.jdbcConnectionFactory.getConnection(Mockito.any(JdbcCredentialProvider.class))).thenReturn(this.connection); + this.secretsManager = Mockito.mock(AWSSecretsManager.class); + Mockito.when(this.secretsManager.getSecretValue(Mockito.eq(new GetSecretValueRequest().withSecretId("testSecret")))).thenReturn(new GetSecretValueResult().withSecretString("{\"username\": \"testUser\", \"password\": \"testPassword\"}")); + this.postGreSqlMetadataHandler = new PostGreSqlMetadataHandler(databaseConnectionConfig, this.secretsManager, this.athena, this.jdbcConnectionFactory); + this.federatedIdentity = Mockito.mock(FederatedIdentity.class); + } + + @Test + public void getPartitionSchema() + { + Assert.assertEquals(SchemaBuilder.newBuilder() + .addField(PostGreSqlMetadataHandler.BLOCK_PARTITION_SCHEMA_COLUMN_NAME, org.apache.arrow.vector.types.Types.MinorType.VARCHAR.getType()) + .addField(PostGreSqlMetadataHandler.BLOCK_PARTITION_COLUMN_NAME, org.apache.arrow.vector.types.Types.MinorType.VARCHAR.getType()).build(), + this.postGreSqlMetadataHandler.getPartitionSchema("testCatalogName")); + } + + @Test + public void doGetTableLayout() + throws Exception + { + BlockAllocator blockAllocator = new BlockAllocatorImpl(); + Constraints constraints = Mockito.mock(Constraints.class); + TableName tableName = new TableName("testSchema", "testTable"); + Schema partitionSchema = this.postGreSqlMetadataHandler.getPartitionSchema("testCatalogName"); + Set partitionCols = partitionSchema.getFields().stream().map(Field::getName).collect(Collectors.toSet()); + GetTableLayoutRequest getTableLayoutRequest = new GetTableLayoutRequest(this.federatedIdentity, "testQueryId", "testCatalogName", tableName, constraints, partitionSchema, partitionCols); + + PreparedStatement preparedStatement = Mockito.mock(PreparedStatement.class); + Mockito.when(this.connection.prepareStatement(PostGreSqlMetadataHandler.GET_PARTITIONS_QUERY)).thenReturn(preparedStatement); + + String[] columns = {"child_schema", "child"}; + int[] types = {Types.VARCHAR, Types.VARCHAR}; + Object[][] values = {{"s0", "p0"}, {"s1", "p1"}}; + ResultSet resultSet = mockResultSet(columns, types, values, new AtomicInteger(-1)); + Mockito.when(preparedStatement.executeQuery()).thenReturn(resultSet); + + Mockito.when(this.connection.getMetaData().getSearchStringEscape()).thenReturn(null); + + GetTableLayoutResponse getTableLayoutResponse = this.postGreSqlMetadataHandler.doGetTableLayout(blockAllocator, getTableLayoutRequest); + + Assert.assertEquals(values.length, getTableLayoutResponse.getPartitions().getRowCount()); + + List expectedValues = new ArrayList<>(); + for (int i = 0; i < getTableLayoutResponse.getPartitions().getRowCount(); i++) { + expectedValues.add(BlockUtils.rowToString(getTableLayoutResponse.getPartitions(), i)); + } + Assert.assertEquals(expectedValues, Arrays.asList("[partition_schema_name : s0], [partition_name : p0]", "[partition_schema_name : s1], [partition_name : p1]")); + + SchemaBuilder expectedSchemaBuilder = SchemaBuilder.newBuilder(); + expectedSchemaBuilder.addField(FieldBuilder.newBuilder(PostGreSqlMetadataHandler.BLOCK_PARTITION_SCHEMA_COLUMN_NAME, org.apache.arrow.vector.types.Types.MinorType.VARCHAR.getType()).build()); + expectedSchemaBuilder.addField(FieldBuilder.newBuilder(PostGreSqlMetadataHandler.BLOCK_PARTITION_COLUMN_NAME, org.apache.arrow.vector.types.Types.MinorType.VARCHAR.getType()).build()); + Schema expectedSchema = expectedSchemaBuilder.build(); + Assert.assertEquals(expectedSchema, getTableLayoutResponse.getPartitions().getSchema()); + Assert.assertEquals(tableName, getTableLayoutResponse.getTableName()); + + Mockito.verify(preparedStatement, Mockito.times(1)).setString(1, tableName.getSchemaName()); + Mockito.verify(preparedStatement, Mockito.times(1)).setString(2, tableName.getTableName()); + } + + @Test + public void doGetTableLayoutWithNoPartitions() + throws Exception + { + BlockAllocator blockAllocator = new BlockAllocatorImpl(); + Constraints constraints = Mockito.mock(Constraints.class); + TableName tableName = new TableName("testSchema", "testTable"); + Schema partitionSchema = this.postGreSqlMetadataHandler.getPartitionSchema("testCatalogName"); + Set partitionCols = partitionSchema.getFields().stream().map(Field::getName).collect(Collectors.toSet()); + GetTableLayoutRequest getTableLayoutRequest = new GetTableLayoutRequest(this.federatedIdentity, "testQueryId", "testCatalogName", tableName, constraints, partitionSchema, partitionCols); + + PreparedStatement preparedStatement = Mockito.mock(PreparedStatement.class); + Mockito.when(this.connection.prepareStatement(PostGreSqlMetadataHandler.GET_PARTITIONS_QUERY)).thenReturn(preparedStatement); + + String[] columns = {"child_schema", "child"}; + int[] types = {Types.VARCHAR, Types.VARCHAR}; + Object[][] values = {{}}; + ResultSet resultSet = mockResultSet(columns, types, values, new AtomicInteger(-1)); + Mockito.when(preparedStatement.executeQuery()).thenReturn(resultSet); + + Mockito.when(this.connection.getMetaData().getSearchStringEscape()).thenReturn(null); + + GetTableLayoutResponse getTableLayoutResponse = this.postGreSqlMetadataHandler.doGetTableLayout(blockAllocator, getTableLayoutRequest); + + Assert.assertEquals(1, getTableLayoutResponse.getPartitions().getRowCount()); + + List expectedValues = new ArrayList<>(); + for (int i = 0; i < getTableLayoutResponse.getPartitions().getRowCount(); i++) { + expectedValues.add(BlockUtils.rowToString(getTableLayoutResponse.getPartitions(), i)); + } + Assert.assertEquals(expectedValues, Collections.singletonList("[partition_schema_name : *], [partition_name : *]")); + + SchemaBuilder expectedSchemaBuilder = SchemaBuilder.newBuilder(); + expectedSchemaBuilder.addField(FieldBuilder.newBuilder(PostGreSqlMetadataHandler.BLOCK_PARTITION_SCHEMA_COLUMN_NAME, org.apache.arrow.vector.types.Types.MinorType.VARCHAR.getType()).build()); + expectedSchemaBuilder.addField(FieldBuilder.newBuilder(PostGreSqlMetadataHandler.BLOCK_PARTITION_COLUMN_NAME, org.apache.arrow.vector.types.Types.MinorType.VARCHAR.getType()).build()); + Schema expectedSchema = expectedSchemaBuilder.build(); + Assert.assertEquals(expectedSchema, getTableLayoutResponse.getPartitions().getSchema()); + Assert.assertEquals(tableName, getTableLayoutResponse.getTableName()); + + Mockito.verify(preparedStatement, Mockito.times(1)).setString(1, tableName.getSchemaName()); + Mockito.verify(preparedStatement, Mockito.times(1)).setString(2, tableName.getTableName()); + } + + @Test(expected = RuntimeException.class) + public void doGetTableLayoutWithSQLException() + throws Exception + { + Constraints constraints = Mockito.mock(Constraints.class); + TableName tableName = new TableName("testSchema", "testTable"); + Schema partitionSchema = this.postGreSqlMetadataHandler.getPartitionSchema("testCatalogName"); + Set partitionCols = partitionSchema.getFields().stream().map(Field::getName).collect(Collectors.toSet()); + GetTableLayoutRequest getTableLayoutRequest = new GetTableLayoutRequest(this.federatedIdentity, "testQueryId", "testCatalogName", tableName, constraints, partitionSchema, partitionCols); + + Connection connection = Mockito.mock(Connection.class, Mockito.RETURNS_DEEP_STUBS); + JdbcConnectionFactory jdbcConnectionFactory = Mockito.mock(JdbcConnectionFactory.class); + Mockito.when(jdbcConnectionFactory.getConnection(Mockito.any(JdbcCredentialProvider.class))).thenReturn(connection); + Mockito.when(connection.getMetaData().getSearchStringEscape()).thenThrow(new SQLException()); + PostGreSqlMetadataHandler postGreSqlMetadataHandler = new PostGreSqlMetadataHandler(databaseConnectionConfig, this.secretsManager, this.athena, jdbcConnectionFactory); + + postGreSqlMetadataHandler.doGetTableLayout(Mockito.mock(BlockAllocator.class), getTableLayoutRequest); + } + + @Test + public void doGetSplits() + throws Exception + { + BlockAllocator blockAllocator = new BlockAllocatorImpl(); + Constraints constraints = Mockito.mock(Constraints.class); + TableName tableName = new TableName("testSchema", "testTable"); + Schema partitionSchema = this.postGreSqlMetadataHandler.getPartitionSchema("testCatalogName"); + Set partitionCols = partitionSchema.getFields().stream().map(Field::getName).collect(Collectors.toSet()); + GetTableLayoutRequest getTableLayoutRequest = new GetTableLayoutRequest(this.federatedIdentity, "testQueryId", "testCatalogName", tableName, constraints, partitionSchema, partitionCols); + + PreparedStatement preparedStatement = Mockito.mock(PreparedStatement.class); + Mockito.when(this.connection.prepareStatement(PostGreSqlMetadataHandler.GET_PARTITIONS_QUERY)).thenReturn(preparedStatement); + + String[] columns = {"child_schema", "child"}; + int[] types = {Types.VARCHAR, Types.VARCHAR}; + Object[][] values = {{"s0", "p0"}, {"s1", "p1"}}; + ResultSet resultSet = mockResultSet(columns, types, values, new AtomicInteger(-1)); + Mockito.when(preparedStatement.executeQuery()).thenReturn(resultSet); + + Mockito.when(this.connection.getMetaData().getSearchStringEscape()).thenReturn(null); + + GetTableLayoutResponse getTableLayoutResponse = this.postGreSqlMetadataHandler.doGetTableLayout(blockAllocator, getTableLayoutRequest); + + BlockAllocator splitBlockAllocator = new BlockAllocatorImpl(); + GetSplitsRequest getSplitsRequest = new GetSplitsRequest(this.federatedIdentity, "testQueryId", "testCatalogName", tableName, getTableLayoutResponse.getPartitions(), new ArrayList<>(partitionCols), constraints, null); + GetSplitsResponse getSplitsResponse = this.postGreSqlMetadataHandler.doGetSplits(splitBlockAllocator, getSplitsRequest); + + Set> expectedSplits = new HashSet<>(); + expectedSplits.add(ImmutableMap.of("partition_schema_name", "s0", "partition_name", "p0")); + expectedSplits.add(ImmutableMap.of("partition_schema_name", "s1", "partition_name", "p1")); + Assert.assertEquals(expectedSplits.size(), getSplitsResponse.getSplits().size()); + Set> actualSplits = getSplitsResponse.getSplits().stream().map(Split::getProperties).collect(Collectors.toSet()); + Assert.assertEquals(expectedSplits, actualSplits); + } + + @Test + public void doGetSplitsContinuation() + throws Exception + { + BlockAllocator blockAllocator = new BlockAllocatorImpl(); + Constraints constraints = Mockito.mock(Constraints.class); + TableName tableName = new TableName("testSchema", "testTable"); + Schema partitionSchema = this.postGreSqlMetadataHandler.getPartitionSchema("testCatalogName"); + Set partitionCols = partitionSchema.getFields().stream().map(Field::getName).collect(Collectors.toSet()); + GetTableLayoutRequest getTableLayoutRequest = new GetTableLayoutRequest(this.federatedIdentity, "testQueryId", "testCatalogName", tableName, constraints, partitionSchema, partitionCols); + + PreparedStatement preparedStatement = Mockito.mock(PreparedStatement.class); + Mockito.when(this.connection.prepareStatement(PostGreSqlMetadataHandler.GET_PARTITIONS_QUERY)).thenReturn(preparedStatement); + + String[] columns = {"child_schema", "child"}; + int[] types = {Types.VARCHAR, Types.VARCHAR}; + Object[][] values = {{"s0", "p0"}, {"s1", "p1"}}; + ResultSet resultSet = mockResultSet(columns, types, values, new AtomicInteger(-1)); + final String expectedQuery = String.format(PostGreSqlMetadataHandler.GET_PARTITIONS_QUERY, tableName.getTableName(), tableName.getSchemaName()); + Mockito.when(preparedStatement.executeQuery()).thenReturn(resultSet); + + Mockito.when(this.connection.getMetaData().getSearchStringEscape()).thenReturn(null); + + GetTableLayoutResponse getTableLayoutResponse = this.postGreSqlMetadataHandler.doGetTableLayout(blockAllocator, getTableLayoutRequest); + + BlockAllocator splitBlockAllocator = new BlockAllocatorImpl(); + GetSplitsRequest getSplitsRequest = new GetSplitsRequest(this.federatedIdentity, "testQueryId", "testCatalogName", tableName, getTableLayoutResponse.getPartitions(), new ArrayList<>(partitionCols), constraints, "1"); + GetSplitsResponse getSplitsResponse = this.postGreSqlMetadataHandler.doGetSplits(splitBlockAllocator, getSplitsRequest); + + Set> expectedSplits = new HashSet<>(); + expectedSplits.add(ImmutableMap.of("partition_schema_name", "s1", "partition_name", "p1")); + Assert.assertEquals(expectedSplits.size(), getSplitsResponse.getSplits().size()); + Set> actualSplits = getSplitsResponse.getSplits().stream().map(Split::getProperties).collect(Collectors.toSet()); + Assert.assertEquals(expectedSplits, actualSplits); + } +} diff --git a/athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/postgresql/PostGreSqlRecordHandlerTest.java b/athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/postgresql/PostGreSqlRecordHandlerTest.java new file mode 100644 index 0000000000..7de4d667fe --- /dev/null +++ b/athena-jdbc/src/test/java/com/amazonaws/connectors/athena/jdbc/postgresql/PostGreSqlRecordHandlerTest.java @@ -0,0 +1,172 @@ +/*- + * #%L + * athena-jdbc + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.connectors.athena.jdbc.postgresql; + +import com.amazonaws.athena.connector.lambda.data.FieldBuilder; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.domain.predicate.Marker; +import com.amazonaws.athena.connector.lambda.domain.predicate.Range; +import com.amazonaws.athena.connector.lambda.domain.predicate.SortedRangeSet; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import com.amazonaws.connectors.athena.jdbc.TestBase; +import com.amazonaws.connectors.athena.jdbc.connection.DatabaseConnectionConfig; +import com.amazonaws.connectors.athena.jdbc.connection.JdbcConnectionFactory; +import com.amazonaws.connectors.athena.jdbc.connection.JdbcCredentialProvider; +import com.amazonaws.connectors.athena.jdbc.manager.JdbcSplitQueryBuilder; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Schema; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.Collections; + +public class PostGreSqlRecordHandlerTest extends TestBase +{ + private PostGreSqlRecordHandler postGreSqlRecordHandler; + private Connection connection; + private JdbcConnectionFactory jdbcConnectionFactory; + private JdbcSplitQueryBuilder jdbcSplitQueryBuilder; + private AmazonS3 amazonS3; + private AWSSecretsManager secretsManager; + private AmazonAthena athena; + + @Before + public void setup() + { + this.amazonS3 = Mockito.mock(AmazonS3.class); + this.secretsManager = Mockito.mock(AWSSecretsManager.class); + this.athena = Mockito.mock(AmazonAthena.class); + this.connection = Mockito.mock(Connection.class); + this.jdbcConnectionFactory = Mockito.mock(JdbcConnectionFactory.class); + Mockito.when(this.jdbcConnectionFactory.getConnection(Mockito.mock(JdbcCredentialProvider.class))).thenReturn(this.connection); + jdbcSplitQueryBuilder = new PostGreSqlQueryStringBuilder("\""); + final DatabaseConnectionConfig databaseConnectionConfig = new DatabaseConnectionConfig("testCatalog", JdbcConnectionFactory.DatabaseEngine.MYSQL, + "mysql://jdbc:mysql://hostname/user=A&password=B"); + + this.postGreSqlRecordHandler = new PostGreSqlRecordHandler(databaseConnectionConfig, amazonS3, secretsManager, athena, jdbcConnectionFactory, jdbcSplitQueryBuilder); + } + + @Test + public void buildSplitSql() + throws SQLException + { + TableName tableName = new TableName("testSchema", "testTable"); + + SchemaBuilder schemaBuilder = SchemaBuilder.newBuilder(); + schemaBuilder.addField(FieldBuilder.newBuilder("testCol1", Types.MinorType.INT.getType()).build()); + schemaBuilder.addField(FieldBuilder.newBuilder("testCol2", Types.MinorType.VARCHAR.getType()).build()); + schemaBuilder.addField(FieldBuilder.newBuilder("testCol3", Types.MinorType.BIGINT.getType()).build()); + schemaBuilder.addField(FieldBuilder.newBuilder("testCol4", Types.MinorType.FLOAT4.getType()).build()); + schemaBuilder.addField(FieldBuilder.newBuilder("testCol5", Types.MinorType.SMALLINT.getType()).build()); + schemaBuilder.addField(FieldBuilder.newBuilder("testCol6", Types.MinorType.TINYINT.getType()).build()); + schemaBuilder.addField(FieldBuilder.newBuilder("testCol7", Types.MinorType.FLOAT8.getType()).build()); + schemaBuilder.addField(FieldBuilder.newBuilder("testCol8", Types.MinorType.BIT.getType()).build()); + schemaBuilder.addField(FieldBuilder.newBuilder("partition_schema_name", Types.MinorType.VARCHAR.getType()).build()); + schemaBuilder.addField(FieldBuilder.newBuilder("partition_name", Types.MinorType.VARCHAR.getType()).build()); + Schema schema = schemaBuilder.build(); + + Split split = Mockito.mock(Split.class); + Mockito.when(split.getProperties()).thenReturn(ImmutableMap.of("partition_schema_name", "s0", "partition_name", "p0")); + Mockito.when(split.getProperty(Mockito.eq(PostGreSqlMetadataHandler.BLOCK_PARTITION_SCHEMA_COLUMN_NAME))).thenReturn("s0"); + Mockito.when(split.getProperty(Mockito.eq(PostGreSqlMetadataHandler.BLOCK_PARTITION_COLUMN_NAME))).thenReturn("p0"); + + Range range1a = Mockito.mock(Range.class, Mockito.RETURNS_DEEP_STUBS); + Mockito.when(range1a.isSingleValue()).thenReturn(true); + Mockito.when(range1a.getLow().getValue()).thenReturn(1); + Range range1b = Mockito.mock(Range.class, Mockito.RETURNS_DEEP_STUBS); + Mockito.when(range1b.isSingleValue()).thenReturn(true); + Mockito.when(range1b.getLow().getValue()).thenReturn(2); + ValueSet valueSet1 = Mockito.mock(SortedRangeSet.class, Mockito.RETURNS_DEEP_STUBS); + Mockito.when(valueSet1.getRanges().getOrderedRanges()).thenReturn(ImmutableList.of(range1a, range1b)); + + ValueSet valueSet2 = getRangeSet(Marker.Bound.EXACTLY, "1", Marker.Bound.BELOW, "10"); + ValueSet valueSet3 = getRangeSet(Marker.Bound.ABOVE, 2L, Marker.Bound.EXACTLY, 20L); + ValueSet valueSet4 = getSingleValueSet(1.1F); + ValueSet valueSet5 = getSingleValueSet(1); + ValueSet valueSet6 = getSingleValueSet(0); + ValueSet valueSet7 = getSingleValueSet(1.2d); + ValueSet valueSet8 = getSingleValueSet(true); + + Constraints constraints = Mockito.mock(Constraints.class); + Mockito.when(constraints.getSummary()).thenReturn(new ImmutableMap.Builder() + .put("testCol1", valueSet1) + .put("testCol2", valueSet2) + .put("testCol3", valueSet3) + .put("testCol4", valueSet4) + .put("testCol5", valueSet5) + .put("testCol6", valueSet6) + .put("testCol7", valueSet7) + .put("testCol8", valueSet8) + .build()); + + String expectedSql = "SELECT \"testCol1\", \"testCol2\", \"testCol3\", \"testCol4\", \"testCol5\", \"testCol6\", \"testCol7\", \"testCol8\" FROM \"s0\".\"p0\" WHERE (\"testCol1\" IN (?,?)) AND ((\"testCol2\" >= ? AND \"testCol2\" < ?)) AND ((\"testCol3\" > ? AND \"testCol3\" <= ?)) AND (\"testCol4\" = ?) AND (\"testCol5\" = ?) AND (\"testCol6\" = ?) AND (\"testCol7\" = ?) AND (\"testCol8\" = ?)"; + PreparedStatement expectedPreparedStatement = Mockito.mock(PreparedStatement.class); + Mockito.when(this.connection.prepareStatement(Mockito.eq(expectedSql))).thenReturn(expectedPreparedStatement); + + PreparedStatement preparedStatement = this.postGreSqlRecordHandler.buildSplitSql(this.connection, "testCatalogName", tableName, schema, constraints, split); + + Assert.assertEquals(expectedPreparedStatement, preparedStatement); + Mockito.verify(preparedStatement, Mockito.times(1)).setInt(1, 1); + Mockito.verify(preparedStatement, Mockito.times(1)).setInt(2, 2); + Mockito.verify(preparedStatement, Mockito.times(1)).setString(3, "1"); + Mockito.verify(preparedStatement, Mockito.times(1)).setString(4, "10"); + Mockito.verify(preparedStatement, Mockito.times(1)).setLong(5, 2L); + Mockito.verify(preparedStatement, Mockito.times(1)).setLong(6, 20L); + Mockito.verify(preparedStatement, Mockito.times(1)).setFloat(7, 1.1F); + Mockito.verify(preparedStatement, Mockito.times(1)).setShort(8, (short) 1); + Mockito.verify(preparedStatement, Mockito.times(1)).setByte(9, (byte) 0); + Mockito.verify(preparedStatement, Mockito.times(1)).setDouble(10, 1.2d); + Mockito.verify(preparedStatement, Mockito.times(1)).setBoolean(11, true); + } + + private ValueSet getSingleValueSet(Object value) { + Range range = Mockito.mock(Range.class, Mockito.RETURNS_DEEP_STUBS); + Mockito.when(range.isSingleValue()).thenReturn(true); + Mockito.when(range.getLow().getValue()).thenReturn(value); + ValueSet valueSet = Mockito.mock(SortedRangeSet.class, Mockito.RETURNS_DEEP_STUBS); + Mockito.when(valueSet.getRanges().getOrderedRanges()).thenReturn(Collections.singletonList(range)); + return valueSet; + } + + private ValueSet getRangeSet(Marker.Bound lowerBound, Object lowerValue, Marker.Bound upperBound, Object upperValue) { + Range range = Mockito.mock(Range.class, Mockito.RETURNS_DEEP_STUBS); + Mockito.when(range.isSingleValue()).thenReturn(false); + Mockito.when(range.getLow().getBound()).thenReturn(lowerBound); + Mockito.when(range.getLow().getValue()).thenReturn(lowerValue); + Mockito.when(range.getHigh().getBound()).thenReturn(upperBound); + Mockito.when(range.getHigh().getValue()).thenReturn(upperValue); + ValueSet valueSet = Mockito.mock(SortedRangeSet.class, Mockito.RETURNS_DEEP_STUBS); + Mockito.when(valueSet.getRanges().getOrderedRanges()).thenReturn(Collections.singletonList(range)); + return valueSet; + } +} diff --git a/athena-redis/LICENSE.txt b/athena-redis/LICENSE.txt new file mode 100644 index 0000000000..418de4c108 --- /dev/null +++ b/athena-redis/LICENSE.txt @@ -0,0 +1,174 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. \ No newline at end of file diff --git a/athena-redis/README.md b/athena-redis/README.md new file mode 100644 index 0000000000..3580770376 --- /dev/null +++ b/athena-redis/README.md @@ -0,0 +1,70 @@ +# Amazon Athena Redis Connector + +This connector enables Amazon Athena to communicate with your Redis instance(s), making your Redis data accessible via SQL. + +Unlike traditional relational data stores, Redis does not have the concept of a table or a column. Instead, Redis offers key-value access patterns where the key is essentially a 'string' and the value is one of: string, z-set, hmap. The Athena Redis Connector allows you to configure virtual tables using the Glue Data Catalog for schema and special table properties to tell the Athena Redis Connector how to map your Redis key-values into a table. You can read more on this below in the 'Setting Up Tables Section'. + + +## Usage + +### Parameters + +The Athena Redis Connector exposes several configuration options via Lambda environment variables. More detail on the available parameters can be found below. + +1. **spill_bucket** - When the data returned by your Lambda function exceeds Lambda’s limits, this is the bucket that the data will be written to for Athena to read the excess from. (e.g. my_bucket) +2. **spill_prefix** - (Optional) Defaults to sub-folder in your bucket called 'athena-federation-spill'. Used in conjunction with spill_bucket, this is the path within the above bucket that large responses are spilled to. You should configure an S3 lifecycle on this location to delete old spills after X days/Hours. +3. **kms_key_id** - (Optional) By default any data that is spilled to S3 is encrypted using AES-GCM and a randomly generated key. Setting a KMS Key ID allows your Lambda function to use KMS for key generation for a stronger source of encryption keys. (e.g. a7e63k4b-8loc-40db-a2a1-4d0en2cd8331) +4. **disable_spill_encryption** - (Optional) Defaults to False so that any data that is spilled to S3 is encrypted using AES-GMC either with a randomly generated key or using KMS to generate keys. Setting this to false will disable spill encryption. You may wish to disable this for improved performance, especially if your spill location in S3 uses S3 Server Side Encryption. (e.g. True or False) +5. **glue_catalog** - (Optional) Can be used to target a cross-account Glue catalog. By default the connector will attempt to get metadata from its own Glue account. + +### Setting Up Databases & Tables + +To enable a Glue Table for use with Redis, you can set the following properties on the Table. redis-endpoint , redis-value-type, and one of redis-keys-zset or redis-key-prefix. Also note that any Glue database which may contain redis tables should have "redis-db-flag" somewhere in the URI property of the Database. You can set this from the Glue Console by editing the database. + +1. **redis-endpoint** - The hostname:port:password of the redis server that data for this table should come from. (e.g. athena-federation-demo.cache.amazonaws.com:6379) Alternatively, you can store the endpoint or part of the endpoint in SecretsManager by using ${secret_name} as the table property value. +2. **redis-keys-zset** - A comma separated list of keys whose value is a zset. Each of the values in the zset is then treated as a key that is part of this table. You must set either this or redis-key-prefix. (e.g. active-orders,pending-orders) +3. **redis-key-prefix** - A comma separated list of key prefixes to scan for values that should be part of this table. You must set either this or redis-keys-zset on the table. (e.g. accounts-*,acct-) +4. **redis-value-type** - (required) Defines how the value for the keys defined by either redis-key-prefix or redis-keys-zset will be mapped to your table. literal maps to a single column. zset also maps to a single column but each key can essentially store N rows. hash allows for each key to be a row with multiple columns. (e.g. hash or literal or zset) + +### Data Types + +All Redis values are retrieved as the basic String data type. From there they are converted to one of the below Apache Arrow data types used by the Athena Query Federation SDK based on how you've defined your table(s) in Glue's DataCatalog. + +|Glue DataType|Apache Arrow Type| +|-------------|-----------------| +|int|INT| +|string|VARCHAR| +|bigint|BIGINT| +|double|FLOAT8| +|float|FLOAT4| +|smallint|SMALLINT| +|tinyint|TINYINT| +|boolean|BIT| +|binary|VARBINARY| + +### Required Permissions + +Review the "Policies" section of the athena-redis.yaml file for full details on the IAM Policies required by this connector. A brief summary is below. + +1. S3 Write Access - In order to successfully handle large queries, the connector requires write access to a location in S3. +2. SecretsManager Read Access - If you choose to store redis-endpoint details in SecretsManager you will need to grant the connector access to those secrets. +3. Glue Data Catalog - Since Redis does not have a meta-data store, the connector requires Read-Only access to Glue's DataCatalog for obtaining Redis key to table/column mappings. +4. VPC Access - In order to connect to your VPC for the purposes of communicating with your Redis instance(s), the connector needs the ability to attach/detach an interface to the VPC. +5. CloudWatch Logs - This is a somewhat implicit permission when deploying a Lambda function but it needs access to cloudwatch logs for storing logs. +1. Athena GetQueryExecution - The connector uses this access to fast-fail when the upstream Athena query has terminated. + +### Deploying The Connector + +To use the Amazon Athena Redis Connector in your queries, navigate to AWS Serverless Application Repository and deploy a pre-built version of this connector. Alternatively, you can build and deploy this connector from source follow the below steps or use the more detailed tutorial in the athena-example module: + +1. From the athena-federation-sdk dir, run `mvn clean install` if you haven't already. +2. From the athena-redis dir, run `mvn clean install`. +3. From the athena-redis dir, run `../tools/publish.sh S3_BUCKET_NAME athena-redis` to publish the connector to your private AWS Serverless Application Repository. The S3_BUCKET in the command is where a copy of the connector's code will be stored for Serverless Application Repository to retrieve it. This will allow users with permission to do so, the ability to deploy instances of the connector via 1-Click form. Then navigate to [Serverless Application Repository](https://aws.amazon.com/serverless/serverlessrepo) + +## Performance + +The Athena Redis Connector will attempt to parallelize queries against your Redis instance depending on the type of table you've defined (zset keys vs. prefix keys). Predicate Pushdown is performed within the Lambda function. + +## License + +This project is licensed under the Apache-2.0 License. \ No newline at end of file diff --git a/athena-redis/athena-redis.yaml b/athena-redis/athena-redis.yaml new file mode 100644 index 0000000000..c2972bddff --- /dev/null +++ b/athena-redis/athena-redis.yaml @@ -0,0 +1,93 @@ +Transform: 'AWS::Serverless-2016-10-31' +Metadata: + 'AWS::ServerlessRepo::Application': + Name: AthenaRedisConnector + Description: 'This connector enables Amazon Athena to communicate with your Redis instance(s), making your Redis data accessible via SQL.' + Author: 'Amazon Athena' + SpdxLicenseId: Apache-2.0 + LicenseUrl: LICENSE.txt + ReadmeUrl: README.md + Labels: + - athena-federation + HomePageUrl: 'https://github.com/awslabs/aws-athena-query-federation' + SemanticVersion: 1.0.0 + SourceCodeUrl: 'https://github.com/awslabs/aws-athena-query-federation' +Parameters: + AthenaCatalogName: + Description: 'The name you will give to this catalog in Athena. It will also be used as the function name.' + Type: String + SpillBucket: + Description: 'The bucket where this function can spill data.' + Type: String + Default: athena-federation-spill + SpillPrefix: + Description: 'The bucket prefix where this function can spill large responses.' + Type: String + Default: athena-spill + LambdaTimeout: + Description: 'Maximum Lambda invocation runtime in seconds. (min 1 - 900 max)' + Default: 900 + Type: Number + LambdaMemory: + Description: 'Lambda memory in MB (min 128 - 3008 max).' + Default: 3008 + Type: Number + DisableSpillEncryption: + Description: "WARNING: If set to 'true' encryption for spilled data is disabled." + Default: 'false' + Type: String + SecurityGroupIds: + Description: 'One or more SecurityGroup IDs corresponding to the SecurityGroup that should be applied to the Lambda function. (e.g. sg1,sg2,sg3)' + Type: 'List' + SubnetIds: + Description: 'One or more Subnet IDs corresponding to the Subnet that the Lambda function can use to access you data source. (e.g. subnet1,subnet2)' + Type: 'List' + SecretNameOrPrefix: + Description: 'The name or prefix of a set of names within Secrets Manager that this function should have access to. (e.g. redis-*).' + Type: String +Resources: + ConnectorConfig: + Type: 'AWS::Serverless::Function' + Properties: + Environment: + Variables: + disable_spill_encryption: !Ref DisableSpillEncryption + spill_bucket: !Ref SpillBucket + spill_prefix: !Ref SpillPrefix + FunctionName: !Ref AthenaCatalogName + Handler: "com.amazonaws.athena.connectors.redis.RedisCompositeHandler" + CodeUri: "./target/athena-redis-1.0.jar" + Description: "Enables Amazon Athena to communicate with Redis, making your Redis data accessible via SQL" + Runtime: java8 + Timeout: !Ref LambdaTimeout + MemorySize: !Ref LambdaMemory + Policies: + - Statement: + - Action: + - secretsmanager:GetSecretValue + Effect: Allow + Resource: !Sub 'arn:aws:secretsmanager:*:*:secret:${SecretNameOrPrefix}' + Version: '2012-10-17' + - Statement: + - Action: + - glue:GetTableVersions + - glue:GetPartitions + - glue:GetTables + - glue:GetTableVersion + - glue:GetDatabases + - glue:GetTable + - glue:GetPartition + - glue:GetDatabase + - athena:GetQueryExecution + Effect: Allow + Resource: '*' + Version: '2012-10-17' + #S3CrudPolicy allows our connector to spill large responses to S3. You can optionally replace this pre-made policy + #with one that is more restrictive and can only 'put' but not read,delete, or overwrite files. + - S3CrudPolicy: + BucketName: !Ref SpillBucket + #VPCAccessPolicy allows our connector to run in a VPC so that it can access your data source. + - VPCAccessPolicy: {} + VpcConfig: + SecurityGroupIds: !Ref SecurityGroupIds + SubnetIds: !Ref SubnetIds \ No newline at end of file diff --git a/athena-redis/pom.xml b/athena-redis/pom.xml new file mode 100644 index 0000000000..eec0b851d7 --- /dev/null +++ b/athena-redis/pom.xml @@ -0,0 +1,61 @@ + + + + aws-athena-query-federation + com.amazonaws + 1.0 + + 4.0.0 + + athena-redis + + + com.amazonaws + aws-athena-federation-sdk + ${aws-athena-federation-sdk.version} + + + redis.clients + jedis + 3.0.0 + + + com.amazonaws + aws-java-sdk-glue + 1.11.490 + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.1 + + false + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + package + + shade + + + + + + + \ No newline at end of file diff --git a/athena-redis/src/main/java/com/amazonaws/athena/connectors/redis/JedisPoolFactory.java b/athena-redis/src/main/java/com/amazonaws/athena/connectors/redis/JedisPoolFactory.java new file mode 100644 index 0000000000..dddbac9dec --- /dev/null +++ b/athena-redis/src/main/java/com/amazonaws/athena/connectors/redis/JedisPoolFactory.java @@ -0,0 +1,87 @@ +/*- + * #%L + * athena-redis + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.redis; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPool; +import redis.clients.jedis.JedisPoolConfig; + +import java.util.HashMap; +import java.util.Map; + +/** + * Creates and Caches JedisPool Instances, using the connection string as the cache key. + * + * @Note Connection String format is expected to be host:port or host:port:password_token + */ +public class JedisPoolFactory +{ + private static final Logger logger = LoggerFactory.getLogger(JedisPoolFactory.class); + + //Realistically we wouldn't need more than 1 but using 4 to give the pool some wiggle room for + //connections that are dying / starting to avoid impacting getting a connection quickly. + private static final int MAX_CONS = 4; + private static final int CONNECTION_TIMEOUT_MS = 2_000; + + private final Map clientCache = new HashMap<>(); + + /** + * Gets or Creates a Jedis instance for the given connection string. + * @param conStr Redis connection details, format is expected to be host:port or host:port:password_token + * @return A Jedis connection if the connection succeeded, else the function will throw. + */ + public synchronized Jedis getOrCreateConn(String conStr) + { + JedisPool pool = clientCache.get(conStr); + if (pool == null) { + String[] endpointParts = conStr.split(":"); + if (endpointParts.length == 2) { + pool = getOrCreateCon(endpointParts[0], Integer.valueOf(endpointParts[1])); + } + else if (endpointParts.length == 3) { + pool = getOrCreateCon(endpointParts[0], Integer.valueOf(endpointParts[1]), endpointParts[2]); + } + else { + throw new IllegalArgumentException("Redis endpoint format error."); + } + + clientCache.put(conStr, pool); + } + return pool.getResource(); + } + + private JedisPool getOrCreateCon(String host, int port) + { + logger.info("getOrCreateCon: Creating connection pool."); + JedisPoolConfig poolConfig = new JedisPoolConfig(); + poolConfig.setMaxTotal(MAX_CONS); + return new JedisPool(poolConfig, host, port, CONNECTION_TIMEOUT_MS); + } + + private JedisPool getOrCreateCon(String host, int port, String passwordToken) + { + logger.info("getOrCreateCon: Creating connection pool with password."); + JedisPoolConfig poolConfig = new JedisPoolConfig(); + poolConfig.setMaxTotal(MAX_CONS); + return new JedisPool(poolConfig, host, port, CONNECTION_TIMEOUT_MS, passwordToken); + } +} diff --git a/athena-redis/src/main/java/com/amazonaws/athena/connectors/redis/KeyType.java b/athena-redis/src/main/java/com/amazonaws/athena/connectors/redis/KeyType.java new file mode 100644 index 0000000000..412ff86b66 --- /dev/null +++ b/athena-redis/src/main/java/com/amazonaws/athena/connectors/redis/KeyType.java @@ -0,0 +1,74 @@ +/*- + * #%L + * athena-redis + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.redis; + +import java.util.HashMap; +import java.util.Map; + +/** + * Defines the support key types that can be used to define the keys that comprise a Redis table in glue. + */ +public enum KeyType +{ + /** + * Indicates that the KeyType is a prefix and so all Redis keys matching this prefix are in scope for the Table. + */ + PREFIX("prefix"), + + /** + * Indicates that the KeyType is a zset and so all Keys that match the value with be zsets and as such we + * should take all the values in those keys and treat them as keys that are in scope for the Table. + * + * For example: my_key_list is a a key which points to a zset that contains: key1, key2, key3. So when I query + * this table. We lookup my_key_list and for each value (key1, key2, key3) in that zset we lookup + * the value. So our table contains the values stored at key1, key2, key3. + */ + ZSET("zset"); + + private static final Map TYPE_MAP = new HashMap<>(); + + static { + for (KeyType next : KeyType.values()) { + TYPE_MAP.put(next.id, next); + } + } + + private String id; + + KeyType(String id) + { + this.id = id; + } + + public String getId() + { + return id; + } + + public static KeyType fromId(String id) + { + KeyType result = TYPE_MAP.get(id); + if (result == null) { + throw new IllegalArgumentException("Unknown KeyType for id: " + id); + } + + return result; + } +} diff --git a/athena-redis/src/main/java/com/amazonaws/athena/connectors/redis/RedisCompositeHandler.java b/athena-redis/src/main/java/com/amazonaws/athena/connectors/redis/RedisCompositeHandler.java new file mode 100644 index 0000000000..b09fb81493 --- /dev/null +++ b/athena-redis/src/main/java/com/amazonaws/athena/connectors/redis/RedisCompositeHandler.java @@ -0,0 +1,35 @@ +/*- + * #%L + * athena-redis + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.redis; + +import com.amazonaws.athena.connector.lambda.handlers.CompositeHandler; + +/** + * Boilerplate composite handler that allows us to use a single Lambda function for both + * Metadata and Data. In this case we just compose RedisMetadataHandler and RedisRecordHandler. + */ +public class RedisCompositeHandler + extends CompositeHandler +{ + public RedisCompositeHandler() + { + super(new RedisMetadataHandler(), new RedisRecordHandler()); + } +} diff --git a/athena-redis/src/main/java/com/amazonaws/athena/connectors/redis/RedisMetadataHandler.java b/athena-redis/src/main/java/com/amazonaws/athena/connectors/redis/RedisMetadataHandler.java new file mode 100644 index 0000000000..ae5f777f47 --- /dev/null +++ b/athena-redis/src/main/java/com/amazonaws/athena/connectors/redis/RedisMetadataHandler.java @@ -0,0 +1,367 @@ +/*- + * #%L + * athena-redis + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.redis; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockUtils; +import com.amazonaws.athena.connector.lambda.data.BlockWriter; +import com.amazonaws.athena.connector.lambda.data.FieldBuilder; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.spill.SpillLocation; +import com.amazonaws.athena.connector.lambda.handlers.GlueMetadataHandler; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesResponse; +import com.amazonaws.athena.connector.lambda.metadata.glue.DefaultGlueType; +import com.amazonaws.athena.connector.lambda.security.EncryptionKeyFactory; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.glue.AWSGlue; +import com.amazonaws.services.glue.AWSGlueClientBuilder; +import com.amazonaws.services.glue.model.Database; +import com.amazonaws.services.glue.model.Table; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import org.apache.arrow.util.VisibleForTesting; +import org.apache.arrow.vector.complex.reader.VarCharReader; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.arrow.vector.util.Text; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.ScanParams; +import redis.clients.jedis.ScanResult; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import static redis.clients.jedis.ScanParams.SCAN_POINTER_START; + +/** + * Handles metadata requests for the Athena Redis Connector using Glue for schema. + *

+ * For more detail, please see the module's README.md, some notable characteristics of this class include: + *

+ * 1. Uses Glue table properties (redis-endpoint, redis-value-type, redis-key-prefix, and redis-keys-zset) to + * provide schema as well as connectivity details to Redis. + * 2. Attempts to resolve sensitive fields such as redis-endpoint via SecretsManager so that you can substitute + * variables with values from by doing something like hostname:port:password=${my_secret} + */ +public class RedisMetadataHandler + extends GlueMetadataHandler +{ + private static final Logger logger = LoggerFactory.getLogger(RedisMetadataHandler.class); + + private static final String SOURCE_TYPE = "redis"; + private static final String END_CURSOR = "0"; + //Controls the max splits to generate, relevant keys are spread across this many splits where possible. + private static final long REDIS_MAX_SPLITS = 10; + //The page size for Jedis scans. + private static final int SCAN_COUNT_SIZE = 100; + protected static final String KEY_COLUMN_NAME = "_key_"; + protected static final String SPLIT_START_INDEX = "start-index"; + protected static final String SPLIT_END_INDEX = "end-index"; + + //Defines the table property name used to set the Redis Key Type for the table. (e.g. prefix, zset) + protected static final String KEY_TYPE = "redis-key-type"; + //Defines the table property name used to set the Redis value type for the table. (e.g. liternal, zset, hash) + protected static final String VALUE_TYPE_TABLE_PROP = "redis-value-type"; + //Defines the table property name used to configure one or more key prefixes to include in the + //table (e.g. key-prefix-1-*, key-prefix-2-*) + protected static final String KEY_PREFIX_TABLE_PROP = "redis-key-prefix"; + //Defines the table property name used to configure one or more zset keys whos values should be used as keys + //to include in the table. + protected static final String ZSET_KEYS_TABLE_PROP = "redis-keys-zset"; + protected static final String KEY_PREFIX_SEPERATOR = ","; + //Defines the table property name used to configure the redis enpoint to query for the data in that table. + //Connection String format is expected to be host:port or host:port:password_token + protected static final String REDIS_ENDPOINT_PROP = "redis-endpoint"; + //Defines the value that should be present in the Glue Database URI to enable the DB for Redis. + protected static final String REDIS_DB_FLAG = "redis-db-flag"; + + //Used to filter out Glue tables which lack a redis endpoint. + private static final TableFilter TABLE_FILTER = (Table table) -> table.getParameters().containsKey(REDIS_ENDPOINT_PROP); + //Used to filter out Glue databases which lack the REDIS_DB_FLAG in the URI. + private static final DatabaseFilter DB_FILTER = (Database database) -> (database.getLocationUri() != null && database.getLocationUri().contains(REDIS_DB_FLAG)); + + private final AWSGlue awsGlue; + private final JedisPoolFactory jedisPoolFactory; + + public RedisMetadataHandler() + { + super(AWSGlueClientBuilder.standard().build(), SOURCE_TYPE); + this.awsGlue = getAwsGlue(); + this.jedisPoolFactory = new JedisPoolFactory(); + } + + @VisibleForTesting + protected RedisMetadataHandler(AWSGlue awsGlue, + EncryptionKeyFactory keyFactory, + AWSSecretsManager secretsManager, + AmazonAthena athena, + JedisPoolFactory jedisPoolFactory, + String spillBucket, + String spillPrefix) + { + super(awsGlue, keyFactory, secretsManager, athena, SOURCE_TYPE, spillBucket, spillPrefix); + this.awsGlue = awsGlue; + this.jedisPoolFactory = jedisPoolFactory; + } + + /** + * Used to obtain a Redis client connection for the provided endpoint. + * + * @param rawEndpoint The value from the REDIS_ENDPOINT_PROP on the table being queried. + * @return A Jedis client connection. + * @notes This method first attempts to resolve any secrets (noted by ${secret_name}) using SecretsManager. + */ + private Jedis getOrCreateClient(String rawEndpoint) + { + String endpoint = resolveSecrets(rawEndpoint); + return jedisPoolFactory.getOrCreateConn(endpoint); + } + + /** + * @see GlueMetadataHandler + */ + @Override + public ListSchemasResponse doListSchemaNames(BlockAllocator blockAllocator, ListSchemasRequest request) + throws Exception + { + return doListSchemaNames(blockAllocator, request, DB_FILTER); + } + + /** + * @see GlueMetadataHandler + */ + @Override + public ListTablesResponse doListTables(BlockAllocator blockAllocator, ListTablesRequest request) + throws Exception + { + return super.doListTables(blockAllocator, request, TABLE_FILTER); + } + + /** + * Retrieves the schema for the request Table from Glue then enriches that result with Redis specific + * metadata and columns. + */ + @Override + public GetTableResponse doGetTable(BlockAllocator blockAllocator, GetTableRequest request) + throws Exception + { + GetTableResponse response = super.doGetTable(blockAllocator, request); + + SchemaBuilder schemaBuilder = SchemaBuilder.newBuilder(); + response.getSchema().getFields().forEach((Field field) -> + schemaBuilder.addField(field.getName(), field.getType(), field.getChildren()) + ); + + response.getSchema().getCustomMetadata().entrySet().forEach((Map.Entry meta) -> + schemaBuilder.addMetadata(meta.getKey(), meta.getValue())); + + schemaBuilder.addField(KEY_COLUMN_NAME, Types.MinorType.VARCHAR.getType()); + + return new GetTableResponse(response.getCatalogName(), response.getTableName(), schemaBuilder.build()); + } + + @Override + public void enhancePartitionSchema(SchemaBuilder partitionSchemaBuilder, GetTableLayoutRequest request) + { + partitionSchemaBuilder.addStringField(REDIS_ENDPOINT_PROP) + .addStringField(VALUE_TYPE_TABLE_PROP) + .addStringField(KEY_PREFIX_TABLE_PROP) + .addStringField(ZSET_KEYS_TABLE_PROP); + } + + /** + * Even though our table doesn't support complex layouts or partitioning, we need to convey that there is at least + * 1 partition to read as part of the query or Athena will assume partition pruning found no candidate layouts to read. + * We also use this 1 partition to carry settings that we will need in order to generate splits. + */ + @Override + public void getPartitions(BlockWriter blockWriter, GetTableLayoutRequest request, QueryStatusChecker queryStatusChecker) + throws Exception + { + Map properties = request.getSchema().getCustomMetadata(); + blockWriter.writeRows((Block block, int rowNum) -> { + block.setValue(REDIS_ENDPOINT_PROP, rowNum, properties.get(REDIS_ENDPOINT_PROP)); + block.setValue(VALUE_TYPE_TABLE_PROP, rowNum, properties.get(VALUE_TYPE_TABLE_PROP)); + block.setValue(KEY_PREFIX_TABLE_PROP, rowNum, properties.get(KEY_PREFIX_TABLE_PROP)); + block.setValue(ZSET_KEYS_TABLE_PROP, rowNum, properties.get(ZSET_KEYS_TABLE_PROP)); + return 1; + }); + } + + /** + * If the table is comprised of multiple key prefixes, then we parallelize those by making them each a split. + * + * @note This function essentially takes each key-prefix and makes it a split. For zset keys, it breaks each zset + * into a max of N split that we have configured to generate as defined by REDIS_MAX_SPLITS. + */ + @Override + public GetSplitsResponse doGetSplits(BlockAllocator blockAllocator, GetSplitsRequest request) + { + if (request.getPartitions().getRowCount() != 1) { + throw new RuntimeException("Unexpected number of partitions encountered."); + } + + Block partitions = request.getPartitions(); + String redisEndpoint = getValue(partitions, 0, REDIS_ENDPOINT_PROP); + String redisValueType = getValue(partitions, 0, VALUE_TYPE_TABLE_PROP); + + logger.info("doGetSplits: Preparing splits for {}", BlockUtils.rowToString(partitions, 0)); + + KeyType keyType = null; + Set splitInputs = new HashSet<>(); + + String keyPrefix = getValue(partitions, 0, KEY_PREFIX_TABLE_PROP); + if (keyPrefix != null) { + //Add the prefixes to the list and set the key type. + splitInputs.addAll(Arrays.asList(keyPrefix.split(KEY_PREFIX_SEPERATOR))); + keyType = KeyType.PREFIX; + } + else { + String[] partitionPrefixes = getValue(partitions, 0, ZSET_KEYS_TABLE_PROP).split(KEY_PREFIX_SEPERATOR); + + ScanResult keyCursor = null; + //Add all the values in the ZSETs ad keys to scan + for (String next : partitionPrefixes) { + do { + keyCursor = loadKeys(redisEndpoint, next, keyCursor, splitInputs); + } + while (keyCursor != null && !END_CURSOR.equals(keyCursor.getCursor())); + } + keyType = KeyType.ZSET; + } + + Set splits = new HashSet<>(); + for (String next : splitInputs) { + splits.addAll(makeSplits(request, redisEndpoint, next, keyType, redisValueType)); + } + + return new GetSplitsResponse(request.getCatalogName(), splits, null); + } + + /** + * For a given key prefix this method attempts to break up all the matching keys into N buckets (aka N splits). + * + * @param request + * @param endpoint The redis endpoint to query. + * @param keyPrefix The key prefix to scan. + * @param keyType The KeyType (prefix or zset). + * @param valueType The ValueType, used for mapping the values stored at each key to a result row when the split is processed. + * @return A Set of splits to optionally parallelize reading the values associated with the keyPrefix. + */ + private Set makeSplits(GetSplitsRequest request, String endpoint, String keyPrefix, KeyType keyType, String valueType) + { + Set splits = new HashSet<>(); + long numberOfKeys = 1; + + if (keyType == KeyType.ZSET) { + try (Jedis client = getOrCreateClient(endpoint)) { + numberOfKeys = client.zcount(keyPrefix, "-inf", "+inf"); + logger.info("makeSplits: ZCOUNT[{}] found [{}]", keyPrefix, numberOfKeys); + } + } + + long stride = (numberOfKeys > REDIS_MAX_SPLITS) ? 1 + (numberOfKeys / REDIS_MAX_SPLITS) : numberOfKeys; + + for (long startIndex = 0; startIndex < numberOfKeys; startIndex += stride) { + long endIndex = startIndex + stride - 1; + if (endIndex >= numberOfKeys) { + endIndex = -1; + } + + //Every split must have a unique location if we wish to spill to avoid failures + SpillLocation spillLocation = makeSpillLocation(request); + + Split split = Split.newBuilder(spillLocation, makeEncryptionKey()) + .add(KEY_PREFIX_TABLE_PROP, keyPrefix) + .add(KEY_TYPE, keyType.getId()) + .add(VALUE_TYPE_TABLE_PROP, valueType) + .add(REDIS_ENDPOINT_PROP, endpoint) + .add(SPLIT_START_INDEX, String.valueOf(startIndex)) + .add(SPLIT_END_INDEX, String.valueOf(endIndex)) + .build(); + + splits.add(split); + + logger.info("makeSplits: Split[{}]", split); + } + + return splits; + } + + /** + * For the given zset prefix, find all values and treat each of those values are a key to scan before returning + * the scan continuation token. + * + * @param connStr The Jedis connection string for the table. + * @param prefix The zset key prefix to scan. + * @param redisCursor The previous Redis cursor (aka continuation token). + * @param keys The collections of keys we collected so far. Any new keys we find are added to this. + * @return The Redis cursor to use when continuing the scan. + */ + private ScanResult loadKeys(String connStr, String prefix, ScanResult redisCursor, Set keys) + { + try (Jedis client = getOrCreateClient(connStr)) { + String cursor = (redisCursor == null) ? SCAN_POINTER_START : redisCursor.getCursor(); + ScanParams scanParam = new ScanParams(); + scanParam.count(SCAN_COUNT_SIZE); + scanParam.match(prefix); + + ScanResult newCursor = client.scan(cursor, scanParam); + keys.addAll(newCursor.getResult()); + return newCursor; + } + } + + /** + * Overrides the default Glue Type to Apache Arrow Type mapping so that we can fail fast on tables which define + * types that are not supported by this connector. + */ + @Override + protected Field convertField(String name, String type) + { + return FieldBuilder.newBuilder(name, DefaultGlueType.fromId(type).getArrowType()).build(); + } + + private String getValue(Block block, int row, String fieldName) + { + VarCharReader reader = block.getFieldReader(fieldName); + reader.setPosition(row); + if (reader.isSet()) { + Text result = reader.readText(); + return (result == null) ? null : result.toString(); + } + + return null; + } +} diff --git a/athena-redis/src/main/java/com/amazonaws/athena/connectors/redis/RedisRecordHandler.java b/athena-redis/src/main/java/com/amazonaws/athena/connectors/redis/RedisRecordHandler.java new file mode 100644 index 0000000000..480640066f --- /dev/null +++ b/athena-redis/src/main/java/com/amazonaws/athena/connectors/redis/RedisRecordHandler.java @@ -0,0 +1,259 @@ +/*- + * #%L + * athena-redis + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.redis; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockSpiller; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.handlers.RecordHandler; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.athena.AmazonAthenaClientBuilder; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.amazonaws.services.secretsmanager.AWSSecretsManagerClientBuilder; +import org.apache.arrow.util.VisibleForTesting; +import org.apache.arrow.vector.types.pojo.Field; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.ScanParams; +import redis.clients.jedis.ScanResult; +import redis.clients.jedis.Tuple; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Collectors; + +import static com.amazonaws.athena.connectors.redis.RedisMetadataHandler.KEY_COLUMN_NAME; +import static com.amazonaws.athena.connectors.redis.RedisMetadataHandler.KEY_PREFIX_TABLE_PROP; +import static com.amazonaws.athena.connectors.redis.RedisMetadataHandler.KEY_TYPE; +import static com.amazonaws.athena.connectors.redis.RedisMetadataHandler.REDIS_ENDPOINT_PROP; +import static com.amazonaws.athena.connectors.redis.RedisMetadataHandler.SPLIT_END_INDEX; +import static com.amazonaws.athena.connectors.redis.RedisMetadataHandler.SPLIT_START_INDEX; +import static com.amazonaws.athena.connectors.redis.RedisMetadataHandler.VALUE_TYPE_TABLE_PROP; +import static redis.clients.jedis.ScanParams.SCAN_POINTER_START; + +/** + * Handles data read record requests for the Athena Redis Connector. + *

+ * For more detail, please see the module's README.md, some notable characteristics of this class include: + *

+ * 1. Supporting literal, zset, and hash value types. + * 2. Attempts to resolve sensitive configuration fields such as redis-endpoint via SecretsManager so that you can + * substitute variables with values from by doing something like hostname:port:password=${my_secret} + */ +public class RedisRecordHandler + extends RecordHandler +{ + private static final Logger logger = LoggerFactory.getLogger(RedisRecordHandler.class); + + private static final String SOURCE_TYPE = "redis"; + private static final String END_CURSOR = "0"; + + //The page size for Jedis scans. + private static final int SCAN_COUNT_SIZE = 100; + + private final JedisPoolFactory jedisPoolFactory; + private final AmazonS3 amazonS3; + + public RedisRecordHandler() + { + this(AmazonS3ClientBuilder.standard().build(), + AWSSecretsManagerClientBuilder.defaultClient(), + AmazonAthenaClientBuilder.defaultClient(), + new JedisPoolFactory()); + } + + @VisibleForTesting + protected RedisRecordHandler(AmazonS3 amazonS3, + AWSSecretsManager secretsManager, + AmazonAthena athena, + JedisPoolFactory jedisPoolFactory) + { + super(amazonS3, secretsManager, athena, SOURCE_TYPE); + this.amazonS3 = amazonS3; + this.jedisPoolFactory = jedisPoolFactory; + } + + /** + * Used to obtain a Redis client connection for the provided endpoint. + * + * @param rawEndpoint The value from the REDIS_ENDPOINT_PROP on the table being queried. + * @return A Jedis client connection. + * @notes This method first attempts to resolve any secrets (noted by ${secret_name}) using SecretsManager. + */ + private Jedis getOrCreateClient(String rawEndpoint) + { + String endpoint = resolveSecrets(rawEndpoint); + return jedisPoolFactory.getOrCreateConn(endpoint); + } + + /** + * @see RecordHandler + */ + @Override + protected void readWithConstraint(BlockSpiller spiller, ReadRecordsRequest recordsRequest, QueryStatusChecker queryStatusChecker) + { + Split split = recordsRequest.getSplit(); + ScanResult keyCursor = null; + + final AtomicLong rowsMatched = new AtomicLong(0); + int numRows = 0; + do { + Set keys = new HashSet<>(); + //Load all the keys associated with this split + keyCursor = loadKeys(split, keyCursor, keys); + + //Scan the data associated with all the keys. + for (String nextKey : keys) { + if (!queryStatusChecker.isQueryRunning()) { + return; + } + try (Jedis client = getOrCreateClient(split.getProperty(REDIS_ENDPOINT_PROP))) { + ValueType valueType = ValueType.fromId(split.getProperty(VALUE_TYPE_TABLE_PROP)); + List fieldList = recordsRequest.getSchema().getFields().stream() + .filter((Field next) -> !KEY_COLUMN_NAME.equals(next.getName())).collect(Collectors.toList()); + + switch (valueType) { + case LITERAL: //The key value is a row with single column + loadLiteralRow(client, nextKey, spiller, fieldList); + break; + case HASH: + loadHashRow(client, nextKey, spiller, fieldList); + break; + case ZSET: + loadZSetRows(client, nextKey, spiller, fieldList); + break; + default: + throw new RuntimeException("Unsupported value type " + valueType); + } + } + } + } + while (keyCursor != null && !END_CURSOR.equals(keyCursor.getCursor())); + } + + /** + * For the given key prefix, find all actual keys depending on the type of the key. + * + * @param split The split for this request, mostly used to get the redis endpoint and config details. + * @param redisCursor The previous Redis cursor (aka continuation token). + * @param keys The collections of keys we collected so far. Any new keys we find are added to this. + * @return The Redis cursor to use when continuing the scan. + */ + private ScanResult loadKeys(Split split, ScanResult redisCursor, Set keys) + { + try (Jedis client = getOrCreateClient(split.getProperty(REDIS_ENDPOINT_PROP))) { + KeyType keyType = KeyType.fromId(split.getProperty(KEY_TYPE)); + String keyPrefix = split.getProperty(KEY_PREFIX_TABLE_PROP); + if (keyType == KeyType.ZSET) { + long start = Long.valueOf(split.getProperty(SPLIT_START_INDEX)); + long end = Long.valueOf(split.getProperty(SPLIT_END_INDEX)); + keys.addAll(client.zrange(keyPrefix, start, end)); + return new ScanResult(END_CURSOR, Collections.EMPTY_LIST); + } + else { + String cursor = (redisCursor == null) ? SCAN_POINTER_START : redisCursor.getCursor(); + ScanParams scanParam = new ScanParams(); + scanParam.count(SCAN_COUNT_SIZE); + scanParam.match(split.getProperty(KEY_PREFIX_TABLE_PROP)); + + ScanResult newCursor = client.scan(cursor, scanParam); + keys.addAll(newCursor.getResult()); + return newCursor; + } + } + } + + private void loadLiteralRow(Jedis client, String keyString, BlockSpiller spiller, List fieldList) + { + spiller.writeRows((Block block, int row) -> { + if (fieldList.size() != 1) { + throw new RuntimeException("Ambiguous field mapping, more than 1 field for literal value type."); + } + + Field field = fieldList.get(0); + Object value = ValueConverter.convert(field, client.get(keyString)); + boolean literalMatched = block.offerValue(KEY_COLUMN_NAME, row, keyString); + literalMatched &= block.offerValue(field.getName(), row, value); + return literalMatched ? 1 : 0; + }); + } + + private void loadHashRow(Jedis client, String keyString, BlockSpiller spiller, List fieldList) + { + spiller.writeRows((Block block, int row) -> { + boolean hashMatched = block.offerValue(KEY_COLUMN_NAME, row, keyString); + + Map rawValues = new HashMap<>(); + //Glue only supports lowercase column names / also could do a better job only fetching the columns + //that are needed + client.hgetAll(keyString).forEach((key, entry) -> rawValues.put(key.toLowerCase(), entry)); + + for (Field hfield : fieldList) { + Object hvalue = ValueConverter.convert(hfield, rawValues.get(hfield.getName())); + if (hashMatched && !block.offerValue(hfield.getName(), row, hvalue)) { + return 0; + } + } + + return 1; + }); + } + + private void loadZSetRows(Jedis client, String keyString, BlockSpiller spiller, List fieldList) + { + if (fieldList.size() != 1) { + throw new RuntimeException("Ambiguous field mapping, more than 1 field for ZSET value type."); + } + + Field zfield = fieldList.get(0); + String cursor = SCAN_POINTER_START; + do { + ScanResult result = client.zscan(keyString, cursor); + cursor = result.getCursor(); + for (Tuple nextElement : result.getResult()) { + spiller.writeRows((Block block, int rowNum) -> { + Object zvalue = ValueConverter.convert(zfield, nextElement.getElement()); + boolean zsetMatched = block.offerValue(KEY_COLUMN_NAME, rowNum, keyString); + zsetMatched &= block.offerValue(zfield.getName(), rowNum, zvalue); + return zsetMatched ? 1 : 0; + }); + } + } + while (cursor != null && !END_CURSOR.equals(cursor)); + } + + /** + * @param split The split for this request, mostly used to get the redis endpoint and config details. + * @param keyString The key to read. + * @param spiller The BlockSpiller to write results into. + * @param startPos The starting postion in the block + * @return The number of rows created in the result block. + */ +} diff --git a/athena-redis/src/main/java/com/amazonaws/athena/connectors/redis/ValueConverter.java b/athena-redis/src/main/java/com/amazonaws/athena/connectors/redis/ValueConverter.java new file mode 100644 index 0000000000..3225f1e0ef --- /dev/null +++ b/athena-redis/src/main/java/com/amazonaws/athena/connectors/redis/ValueConverter.java @@ -0,0 +1,77 @@ +/*- + * #%L + * athena-redis + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.redis; + +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.arrow.vector.types.pojo.Field; + +import java.io.UnsupportedEncodingException; + +/** + * Used to convert from Redis' native value/type system to the Apache Arrow type that was configured + * for the particular field. + */ +public class ValueConverter +{ + private ValueConverter() {} + + /** + * Allows for coercing types in the event that schema has evolved or there were other data issues. + * @param field The Apache Arrow field that the value belongs to. + * @param origVal The original value from Redis (before any conversion or coercion). + * @return The coerced value. + */ + public static Object convert(Field field, String origVal) + { + if (origVal == null) { + return origVal; + } + + ArrowType arrowType = field.getType(); + Types.MinorType minorType = Types.getMinorTypeForArrowType(arrowType); + + switch (minorType) { + case VARCHAR: + return origVal; + case INT: + case SMALLINT: + case TINYINT: + return Integer.valueOf(origVal); + case BIGINT: + return Long.valueOf(origVal); + case FLOAT8: + return Double.valueOf(origVal); + case FLOAT4: + return Float.valueOf(origVal); + case BIT: + return Boolean.valueOf(origVal); + case VARBINARY: + try { + return origVal.getBytes("UTF-8"); + } + catch (UnsupportedEncodingException ex) { + throw new RuntimeException(ex); + } + default: + throw new RuntimeException("Unsupported type conversation " + minorType + " field: " + field.getName()); + } + } +} diff --git a/athena-redis/src/main/java/com/amazonaws/athena/connectors/redis/ValueType.java b/athena-redis/src/main/java/com/amazonaws/athena/connectors/redis/ValueType.java new file mode 100644 index 0000000000..636dd9b869 --- /dev/null +++ b/athena-redis/src/main/java/com/amazonaws/athena/connectors/redis/ValueType.java @@ -0,0 +1,74 @@ +/*- + * #%L + * athena-redis + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.redis; + +import java.util.HashMap; +import java.util.Map; + +/** + * Defines the supported value types that can be used to define a Redis table in Glue and thus mapped to rows. + */ +public enum ValueType +{ + /** + * The value is a single, literal value which requires no interpretation before conversion. + */ + LITERAL("literal"), + /** + * The value is actually a set of literal values and so we should treat the value as a list of rows, converting + * each value independently. + */ + ZSET("zset"), + /** + * The value is a single multi-column row and the values in the hash should be mapped to columns in the table but each + * value is still 1 row. + */ + HASH("hash"); + + private static final Map TYPE_MAP = new HashMap<>(); + + static { + for (ValueType next : ValueType.values()) { + TYPE_MAP.put(next.id, next); + } + } + + private String id; + + ValueType(String id) + { + this.id = id; + } + + public String getId() + { + return id; + } + + public static ValueType fromId(String id) + { + ValueType result = TYPE_MAP.get(id); + if (result == null) { + throw new IllegalArgumentException("Unknown ValueType for id: " + id); + } + + return result; + } +} diff --git a/athena-redis/src/test/java/com/amazonaws/athena/connectors/redis/RedisMetadataHandlerTest.java b/athena-redis/src/test/java/com/amazonaws/athena/connectors/redis/RedisMetadataHandlerTest.java new file mode 100644 index 0000000000..02b9c9e0b4 --- /dev/null +++ b/athena-redis/src/test/java/com/amazonaws/athena/connectors/redis/RedisMetadataHandlerTest.java @@ -0,0 +1,279 @@ +/*- + * #%L + * athena-redis + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.redis; + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.amazonaws.athena.connector.lambda.data.BlockUtils; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutResponse; +import com.amazonaws.athena.connector.lambda.metadata.MetadataRequestType; +import com.amazonaws.athena.connector.lambda.metadata.MetadataResponse; +import com.amazonaws.athena.connector.lambda.security.FederatedIdentity; +import com.amazonaws.athena.connector.lambda.security.LocalKeyFactory; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.glue.AWSGlue; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.amazonaws.services.secretsmanager.model.GetSecretValueRequest; +import com.amazonaws.services.secretsmanager.model.GetSecretValueResult; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Schema; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.runners.MockitoJUnitRunner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.ScanParams; +import redis.clients.jedis.ScanResult; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import static com.amazonaws.athena.connectors.redis.RedisMetadataHandler.KEY_PREFIX_TABLE_PROP; +import static com.amazonaws.athena.connectors.redis.RedisMetadataHandler.REDIS_ENDPOINT_PROP; +import static com.amazonaws.athena.connectors.redis.RedisMetadataHandler.VALUE_TYPE_TABLE_PROP; +import static com.amazonaws.athena.connectors.redis.RedisMetadataHandler.ZSET_KEYS_TABLE_PROP; +import static org.junit.Assert.*; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class RedisMetadataHandlerTest +{ + private static final Logger logger = LoggerFactory.getLogger(RedisMetadataHandlerTest.class); + + private FederatedIdentity identity = new FederatedIdentity("id", "principal", "account"); + private String endpoint = "${endpoint}"; + private String decodedEndpoint = "endpoint:123"; + private RedisMetadataHandler handler; + private BlockAllocator allocator; + + @Mock + private Jedis mockClient; + + @Mock + private AWSGlue mockGlue; + + @Mock + private AWSSecretsManager mockSecretsManager; + + @Mock + private AmazonAthena mockAthena; + + @Mock + private JedisPoolFactory mockFactory; + + @Before + public void setUp() + throws Exception + { + when(mockFactory.getOrCreateConn(eq(decodedEndpoint))).thenReturn(mockClient); + + handler = new RedisMetadataHandler(mockGlue, new LocalKeyFactory(), mockSecretsManager, mockAthena, mockFactory, "bucket", "prefix"); + allocator = new BlockAllocatorImpl(); + + when(mockSecretsManager.getSecretValue(any(GetSecretValueRequest.class))) + .thenAnswer((InvocationOnMock invocation) -> { + GetSecretValueRequest request = invocation.getArgumentAt(0, GetSecretValueRequest.class); + if ("endpoint".equalsIgnoreCase(request.getSecretId())) { + return new GetSecretValueResult().withSecretString(decodedEndpoint); + } + throw new RuntimeException("Unknown secret " + request.getSecretId()); + }); + } + + @After + public void tearDown() + throws Exception + { + allocator.close(); + } + + @Test + public void doGetTableLayout() + throws Exception + { + logger.info("doGetTableLayout - enter"); + + Schema schema = SchemaBuilder.newBuilder().build(); + + GetTableLayoutRequest req = new GetTableLayoutRequest(identity, "queryId", "default", + new TableName("schema1", "table1"), + new Constraints(new HashMap<>()), + schema, + new HashSet<>()); + + GetTableLayoutResponse res = handler.doGetTableLayout(allocator, req); + + logger.info("doGetTableLayout - {}", res); + Block partitions = res.getPartitions(); + for (int row = 0; row < partitions.getRowCount() && row < 10; row++) { + logger.info("doGetTableLayout:{} {}", row, BlockUtils.rowToString(partitions, row)); + } + + assertTrue(partitions.getRowCount() > 0); + assertEquals(4, partitions.getFields().size()); + + logger.info("doGetTableLayout: partitions[{}]", partitions.getRowCount()); + } + + @Test + public void doGetSplitsZset() + { + logger.info("doGetSplitsPrefix: enter"); + + //3 prefixes for this table + String prefixes = "prefix1-*,prefix2-*, prefix3-*"; + + //4 zsets per prefix + when(mockClient.scan(anyString(), any(ScanParams.class))).then((InvocationOnMock invocationOnMock) -> { + String cursor = (String) invocationOnMock.getArguments()[0]; + if (cursor == null || cursor.equals("0")) { + List result = new ArrayList<>(); + result.add(UUID.randomUUID().toString()); + result.add(UUID.randomUUID().toString()); + result.add(UUID.randomUUID().toString()); + return new ScanResult<>("1", result); + } + else { + List result = new ArrayList<>(); + result.add(UUID.randomUUID().toString()); + return new ScanResult<>("0", result); + } + }); + + //100 keys per zset + when(mockClient.zcount(anyString(), anyString(), anyString())).thenReturn(200L); + + List partitionCols = new ArrayList<>(); + + Schema schema = SchemaBuilder.newBuilder() + .addField("partitionId", Types.MinorType.INT.getType()) + .addStringField(REDIS_ENDPOINT_PROP) + .addStringField(VALUE_TYPE_TABLE_PROP) + .addStringField(KEY_PREFIX_TABLE_PROP) + .addStringField(ZSET_KEYS_TABLE_PROP) + .build(); + + Block partitions = allocator.createBlock(schema); + partitions.setValue(REDIS_ENDPOINT_PROP, 0, endpoint); + partitions.setValue(VALUE_TYPE_TABLE_PROP, 0, null); + partitions.setValue(KEY_PREFIX_TABLE_PROP, 0, null); + partitions.setValue(ZSET_KEYS_TABLE_PROP, 0, prefixes); + partitions.setRowCount(1); + + String continuationToken = null; + GetSplitsRequest originalReq = new GetSplitsRequest(identity, + "queryId", + "catalog_name", + new TableName("schema", "table_name"), + partitions, + partitionCols, + new Constraints(new HashMap<>()), + null); + + GetSplitsRequest req = new GetSplitsRequest(originalReq, continuationToken); + + logger.info("doGetSplitsPrefix: req[{}]", req); + + MetadataResponse rawResponse = handler.doGetSplits(allocator, req); + assertEquals(MetadataRequestType.GET_SPLITS, rawResponse.getRequestType()); + + GetSplitsResponse response = (GetSplitsResponse) rawResponse; + continuationToken = response.getContinuationToken(); + + logger.info("doGetSplitsPrefix: continuationToken[{}] - numSplits[{}]", + new Object[] {continuationToken, response.getSplits().size()}); + + assertEquals("Continuation criteria violated", 120, response.getSplits().size()); + assertTrue("Continuation criteria violated", response.getContinuationToken() == null); + + verify(mockClient, times(6)).scan(anyString(), any(ScanParams.class)); + logger.info("doGetSplitsPrefix: exit"); + } + + @Test + public void doGetSplitsPrefix() + { + logger.info("doGetSplitsPrefix: enter"); + + Schema schema = SchemaBuilder.newBuilder() + .addField("partitionId", Types.MinorType.INT.getType()) + .addStringField(REDIS_ENDPOINT_PROP) + .addStringField(VALUE_TYPE_TABLE_PROP) + .addStringField(KEY_PREFIX_TABLE_PROP) + .addStringField(ZSET_KEYS_TABLE_PROP) + .build(); + + Block partitions = allocator.createBlock(schema); + partitions.setValue(REDIS_ENDPOINT_PROP, 0, endpoint); + partitions.setValue(VALUE_TYPE_TABLE_PROP, 0, null); + partitions.setValue(KEY_PREFIX_TABLE_PROP, 0, "prefix1-*,prefix2-*, prefix3-*"); + partitions.setValue(ZSET_KEYS_TABLE_PROP, 0, null); + partitions.setRowCount(1); + + String continuationToken = null; + GetSplitsRequest originalReq = new GetSplitsRequest(identity, + "queryId", + "catalog_name", + new TableName("schema", "table_name"), + partitions, + new ArrayList<>(), + new Constraints(new HashMap<>()), + null); + + GetSplitsRequest req = new GetSplitsRequest(originalReq, continuationToken); + + logger.info("doGetSplitsPrefix: req[{}]", req); + + MetadataResponse rawResponse = handler.doGetSplits(allocator, req); + assertEquals(MetadataRequestType.GET_SPLITS, rawResponse.getRequestType()); + + GetSplitsResponse response = (GetSplitsResponse) rawResponse; + continuationToken = response.getContinuationToken(); + + logger.info("doGetSplitsPrefix: continuationToken[{}] - numSplits[{}]", + new Object[] {continuationToken, response.getSplits().size()}); + + assertTrue("Continuation criteria violated", response.getSplits().size() == 3); + assertTrue("Continuation criteria violated", response.getContinuationToken() == null); + + logger.info("doGetSplitsPrefix: exit"); + } +} diff --git a/athena-redis/src/test/java/com/amazonaws/athena/connectors/redis/RedisRecordHandlerTest.java b/athena-redis/src/test/java/com/amazonaws/athena/connectors/redis/RedisRecordHandlerTest.java new file mode 100644 index 0000000000..4183ed6c11 --- /dev/null +++ b/athena-redis/src/test/java/com/amazonaws/athena/connectors/redis/RedisRecordHandlerTest.java @@ -0,0 +1,475 @@ +/*- + * #%L + * athena-redis + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.redis; + +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.amazonaws.athena.connector.lambda.data.BlockUtils; +import com.amazonaws.athena.connector.lambda.data.S3BlockSpillReader; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.domain.predicate.Range; +import com.amazonaws.athena.connector.lambda.domain.predicate.SortedRangeSet; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import com.amazonaws.athena.connector.lambda.domain.spill.S3SpillLocation; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsResponse; +import com.amazonaws.athena.connector.lambda.records.RecordResponse; +import com.amazonaws.athena.connector.lambda.security.EncryptionKeyFactory; +import com.amazonaws.athena.connector.lambda.security.FederatedIdentity; +import com.amazonaws.athena.connector.lambda.security.LocalKeyFactory; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.PutObjectResult; +import com.amazonaws.services.s3.model.S3Object; +import com.amazonaws.services.s3.model.S3ObjectInputStream; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.amazonaws.services.secretsmanager.model.GetSecretValueRequest; +import com.amazonaws.services.secretsmanager.model.GetSecretValueResult; +import com.google.common.collect.ImmutableList; +import com.google.common.io.ByteStreams; +import org.apache.arrow.vector.complex.reader.FieldReader; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Schema; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.runners.MockitoJUnitRunner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.ScanParams; +import redis.clients.jedis.ScanResult; +import redis.clients.jedis.Tuple; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicLong; + +import static com.amazonaws.athena.connectors.redis.RedisMetadataHandler.KEY_COLUMN_NAME; +import static com.amazonaws.athena.connectors.redis.RedisMetadataHandler.KEY_PREFIX_TABLE_PROP; +import static com.amazonaws.athena.connectors.redis.RedisMetadataHandler.KEY_TYPE; +import static com.amazonaws.athena.connectors.redis.RedisMetadataHandler.REDIS_ENDPOINT_PROP; +import static com.amazonaws.athena.connectors.redis.RedisMetadataHandler.VALUE_TYPE_TABLE_PROP; +import static org.junit.Assert.*; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class RedisRecordHandlerTest +{ + private static final Logger logger = LoggerFactory.getLogger(RedisRecordHandlerTest.class); + + private FederatedIdentity identity = new FederatedIdentity("id", "principal", "account"); + private String endpoint = "${endpoint}"; + private String decodedEndpoint = "endpoint:123"; + private RedisRecordHandler handler; + private BlockAllocator allocator; + private List mockS3Storage = new ArrayList<>(); + private AmazonS3 amazonS3; + private S3BlockSpillReader spillReader; + private EncryptionKeyFactory keyFactory = new LocalKeyFactory(); + + @Mock + private Jedis mockClient; + + @Mock + private AWSSecretsManager mockSecretsManager; + + @Mock + private JedisPoolFactory mockFactory; + + @Mock + private AmazonAthena mockAthena; + + @Before + public void setUp() + { + logger.info("setUpBefore - enter"); + when(mockFactory.getOrCreateConn(eq(decodedEndpoint))).thenReturn(mockClient); + + allocator = new BlockAllocatorImpl(); + + amazonS3 = mock(AmazonS3.class); + + when(amazonS3.putObject(anyObject(), anyObject(), anyObject(), anyObject())) + .thenAnswer((InvocationOnMock invocationOnMock) -> { + InputStream inputStream = (InputStream) invocationOnMock.getArguments()[2]; + ByteHolder byteHolder = new ByteHolder(); + byteHolder.setBytes(ByteStreams.toByteArray(inputStream)); + mockS3Storage.add(byteHolder); + return mock(PutObjectResult.class); + }); + + when(amazonS3.getObject(anyString(), anyString())) + .thenAnswer((InvocationOnMock invocationOnMock) -> { + S3Object mockObject = mock(S3Object.class); + ByteHolder byteHolder = mockS3Storage.get(0); + mockS3Storage.remove(0); + when(mockObject.getObjectContent()).thenReturn( + new S3ObjectInputStream( + new ByteArrayInputStream(byteHolder.getBytes()), null)); + return mockObject; + }); + + when(mockSecretsManager.getSecretValue(any(GetSecretValueRequest.class))) + .thenAnswer((InvocationOnMock invocation) -> { + GetSecretValueRequest request = invocation.getArgumentAt(0, GetSecretValueRequest.class); + if ("endpoint".equalsIgnoreCase(request.getSecretId())) { + return new GetSecretValueResult().withSecretString(decodedEndpoint); + } + throw new RuntimeException("Unknown secret " + request.getSecretId()); + }); + + handler = new RedisRecordHandler(amazonS3, mockSecretsManager, mockAthena, mockFactory); + spillReader = new S3BlockSpillReader(amazonS3, allocator); + + logger.info("setUpBefore - exit"); + } + + @After + public void after() + { + allocator.close(); + } + + @Test + public void doReadRecordsLiteral() + throws Exception + { + logger.info("doReadRecordsLiteral: enter"); + + //4 keys per prefix + when(mockClient.scan(anyString(), any(ScanParams.class))).then((InvocationOnMock invocationOnMock) -> { + String cursor = (String) invocationOnMock.getArguments()[0]; + if (cursor == null || cursor.equals("0")) { + List result = new ArrayList<>(); + result.add(UUID.randomUUID().toString()); + result.add(UUID.randomUUID().toString()); + result.add(UUID.randomUUID().toString()); + return new ScanResult<>("1", result); + } + else { + List result = new ArrayList<>(); + result.add(UUID.randomUUID().toString()); + return new ScanResult<>("0", result); + } + }); + + AtomicLong value = new AtomicLong(0); + when(mockClient.get(anyString())) + .thenAnswer((InvocationOnMock invocationOnMock) -> String.valueOf(value.getAndIncrement())); + + String catalog = "catalog1"; + String schema = "schema1"; + String table = "table1"; + + S3SpillLocation splitLoc = S3SpillLocation.newBuilder() + .withBucket(UUID.randomUUID().toString()) + .withSplitId(UUID.randomUUID().toString()) + .withQueryId(UUID.randomUUID().toString()) + .withIsDirectory(true) + .build(); + + Split split = Split.newBuilder(splitLoc, keyFactory.create()) + .add(REDIS_ENDPOINT_PROP, endpoint) + .add(KEY_TYPE, KeyType.PREFIX.getId()) + .add(KEY_PREFIX_TABLE_PROP, "key-*") + .add(VALUE_TYPE_TABLE_PROP, ValueType.LITERAL.getId()) + .build(); + + Schema schemaForRead = SchemaBuilder.newBuilder() + .addField("_key_", Types.MinorType.VARCHAR.getType()) + .addField("intcol", Types.MinorType.INT.getType()) + .build(); + + Map constraintsMap = new HashMap<>(); + constraintsMap.put("intcol", SortedRangeSet.copyOf(Types.MinorType.INT.getType(), + ImmutableList.of(Range.greaterThan(allocator, Types.MinorType.INT.getType(), 1)), false)); + + ReadRecordsRequest request = new ReadRecordsRequest(identity, + catalog, + "queryId-" + System.currentTimeMillis(), + new TableName(schema, table), + schemaForRead, + split, + new Constraints(constraintsMap), + 100_000_000_000L, //100GB don't expect this to spill + 100_000_000_000L + ); + + RecordResponse rawResponse = handler.doReadRecords(allocator, request); + + assertTrue(rawResponse instanceof ReadRecordsResponse); + + ReadRecordsResponse response = (ReadRecordsResponse) rawResponse; + logger.info("doReadRecordsLiteral: rows[{}]", response.getRecordCount()); + + logger.info("doReadRecordsLiteral: {}", BlockUtils.rowToString(response.getRecords(), 0)); + assertTrue(response.getRecords().getRowCount() == 2); + + FieldReader keyReader = response.getRecords().getFieldReader(KEY_COLUMN_NAME); + keyReader.setPosition(0); + assertNotNull(keyReader.readText().toString()); + + FieldReader intCol = response.getRecords().getFieldReader("intcol"); + intCol.setPosition(0); + assertNotNull(intCol.readInteger()); + + logger.info("doReadRecordsLiteral: exit"); + } + + @Test + public void doReadRecordsHash() + throws Exception + { + logger.info("doReadRecordsHash: enter"); + + //4 keys per prefix + when(mockClient.scan(anyString(), any(ScanParams.class))).then((InvocationOnMock invocationOnMock) -> { + String cursor = (String) invocationOnMock.getArguments()[0]; + if (cursor == null || cursor.equals("0")) { + List result = new ArrayList<>(); + result.add(UUID.randomUUID().toString()); + result.add(UUID.randomUUID().toString()); + result.add(UUID.randomUUID().toString()); + result.add(UUID.randomUUID().toString()); + result.add(UUID.randomUUID().toString()); + return new ScanResult<>("1", result); + } + else { + List result = new ArrayList<>(); + result.add(UUID.randomUUID().toString()); + result.add(UUID.randomUUID().toString()); + return new ScanResult<>("0", result); + } + }); + + //4 columns per key + AtomicLong intColVal = new AtomicLong(0); + when(mockClient.hgetAll(anyString())).then((InvocationOnMock invocationOnMock) -> { + Map result = new HashMap<>(); + result.put("intcol", String.valueOf(intColVal.getAndIncrement())); + result.put("stringcol", UUID.randomUUID().toString()); + result.put("extracol", UUID.randomUUID().toString()); + return result; + }); + + AtomicLong value = new AtomicLong(0); + when(mockClient.get(anyString())) + .thenAnswer((InvocationOnMock invocationOnMock) -> String.valueOf(value.getAndIncrement())); + + String catalog = "catalog1"; + String schema = "schema1"; + String table = "table1"; + + S3SpillLocation splitLoc = S3SpillLocation.newBuilder() + .withBucket(UUID.randomUUID().toString()) + .withSplitId(UUID.randomUUID().toString()) + .withQueryId(UUID.randomUUID().toString()) + .withIsDirectory(true) + .build(); + + Split split = Split.newBuilder(splitLoc, keyFactory.create()) + .add(REDIS_ENDPOINT_PROP, endpoint) + .add(KEY_TYPE, KeyType.PREFIX.getId()) + .add(KEY_PREFIX_TABLE_PROP, "key-*") + .add(VALUE_TYPE_TABLE_PROP, ValueType.HASH.getId()) + .build(); + + Schema schemaForRead = SchemaBuilder.newBuilder() + .addField("_key_", Types.MinorType.VARCHAR.getType()) + .addField("intcol", Types.MinorType.INT.getType()) + .addField("stringcol", Types.MinorType.VARCHAR.getType()) + .build(); + + Map constraintsMap = new HashMap<>(); + constraintsMap.put("intcol", SortedRangeSet.copyOf(Types.MinorType.INT.getType(), + ImmutableList.of(Range.greaterThan(allocator, Types.MinorType.INT.getType(), 1)), false)); + + ReadRecordsRequest request = new ReadRecordsRequest(identity, + catalog, + "queryId-" + System.currentTimeMillis(), + new TableName(schema, table), + schemaForRead, + split, + new Constraints(constraintsMap), + 100_000_000_000L, //100GB don't expect this to spill + 100_000_000_000L + ); + + RecordResponse rawResponse = handler.doReadRecords(allocator, request); + + assertTrue(rawResponse instanceof ReadRecordsResponse); + + ReadRecordsResponse response = (ReadRecordsResponse) rawResponse; + logger.info("doReadRecordsHash: rows[{}]", response.getRecordCount()); + + logger.info("doReadRecordsHash: {}", BlockUtils.rowToString(response.getRecords(), 0)); + assertTrue(response.getRecords().getRowCount() == 5); + assertTrue(response.getRecords().getFields().size() == schemaForRead.getFields().size()); + + FieldReader keyReader = response.getRecords().getFieldReader(KEY_COLUMN_NAME); + keyReader.setPosition(0); + assertNotNull(keyReader.readText()); + + FieldReader intCol = response.getRecords().getFieldReader("intcol"); + intCol.setPosition(0); + assertNotNull(intCol.readInteger()); + + FieldReader stringCol = response.getRecords().getFieldReader("stringcol"); + stringCol.setPosition(0); + assertNotNull(stringCol.readText()); + + logger.info("doReadRecordsHash: exit"); + } + + @Test + public void doReadRecordsZset() + throws Exception + { + logger.info("doReadRecordsZset: enter"); + + //4 keys per prefix + when(mockClient.scan(anyString(), any(ScanParams.class))).then((InvocationOnMock invocationOnMock) -> { + String cursor = (String) invocationOnMock.getArguments()[0]; + if (cursor == null || cursor.equals("0")) { + List result = new ArrayList<>(); + result.add(UUID.randomUUID().toString()); + result.add(UUID.randomUUID().toString()); + result.add(UUID.randomUUID().toString()); + return new ScanResult<>("1", result); + } + else { + List result = new ArrayList<>(); + result.add(UUID.randomUUID().toString()); + return new ScanResult<>("0", result); + } + }); + + //4 rows per key + when(mockClient.zscan(anyString(), anyString())).then((InvocationOnMock invocationOnMock) -> { + String cursor = (String) invocationOnMock.getArguments()[1]; + if (cursor == null || cursor.equals("0")) { + List result = new ArrayList<>(); + result.add(new Tuple("1", 0.0D)); + result.add(new Tuple("2", 0.0D)); + result.add(new Tuple("3", 0.0D)); + return new ScanResult<>("1", result); + } + else { + List result = new ArrayList<>(); + result.add(new Tuple("4", 0.0D)); + return new ScanResult<>("0", result); + } + }); + + AtomicLong value = new AtomicLong(0); + when(mockClient.get(anyString())) + .thenAnswer((InvocationOnMock invocationOnMock) -> String.valueOf(value.getAndIncrement())); + + String catalog = "catalog1"; + String schema = "schema1"; + String table = "table1"; + + S3SpillLocation splitLoc = S3SpillLocation.newBuilder() + .withBucket(UUID.randomUUID().toString()) + .withSplitId(UUID.randomUUID().toString()) + .withQueryId(UUID.randomUUID().toString()) + .withIsDirectory(true) + .build(); + + Split split = Split.newBuilder(splitLoc, keyFactory.create()) + .add(REDIS_ENDPOINT_PROP, endpoint) + .add(KEY_TYPE, KeyType.PREFIX.getId()) + .add(KEY_PREFIX_TABLE_PROP, "key-*") + .add(VALUE_TYPE_TABLE_PROP, ValueType.ZSET.getId()) + .build(); + + Schema schemaForRead = SchemaBuilder.newBuilder() + .addField("_key_", Types.MinorType.VARCHAR.getType()) + .addField("intcol", Types.MinorType.INT.getType()) + .build(); + + Map constraintsMap = new HashMap<>(); + constraintsMap.put("intcol", SortedRangeSet.copyOf(Types.MinorType.INT.getType(), + ImmutableList.of(Range.greaterThan(allocator, Types.MinorType.INT.getType(), 1)), false)); + + ReadRecordsRequest request = new ReadRecordsRequest(identity, + catalog, + "queryId-" + System.currentTimeMillis(), + new TableName(schema, table), + schemaForRead, + split, + new Constraints(constraintsMap), + 100_000_000_000L, //100GB don't expect this to spill + 100_000_000_000L + ); + + RecordResponse rawResponse = handler.doReadRecords(allocator, request); + + assertTrue(rawResponse instanceof ReadRecordsResponse); + + ReadRecordsResponse response = (ReadRecordsResponse) rawResponse; + logger.info("doReadRecordsZset: rows[{}]", response.getRecordCount()); + + logger.info("doReadRecordsZset: {}", BlockUtils.rowToString(response.getRecords(), 0)); + assertTrue(response.getRecords().getRowCount() == 12); + + FieldReader keyReader = response.getRecords().getFieldReader(KEY_COLUMN_NAME); + keyReader.setPosition(0); + assertNotNull(keyReader.readText()); + + FieldReader intCol = response.getRecords().getFieldReader("intcol"); + intCol.setPosition(0); + assertNotNull(intCol.readInteger()); + + logger.info("doReadRecordsZset: exit"); + } + + private class ByteHolder + { + private byte[] bytes; + + public void setBytes(byte[] bytes) + { + this.bytes = bytes; + } + + public byte[] getBytes() + { + return bytes; + } + } +} diff --git a/athena-tpcds/LICENSE.txt b/athena-tpcds/LICENSE.txt new file mode 100644 index 0000000000..67db858821 --- /dev/null +++ b/athena-tpcds/LICENSE.txt @@ -0,0 +1,175 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. diff --git a/athena-tpcds/README.md b/athena-tpcds/README.md new file mode 100644 index 0000000000..7013a5e437 --- /dev/null +++ b/athena-tpcds/README.md @@ -0,0 +1,135 @@ +# Amazon Athena TPC-DS Connector + +This connector enables Amazon Athena to communicate with a source of randomly generated TPC-DS data for use in benchmarking and functional testing. + +## Usage + +### Parameters + +The Athena TPC-DS Connector exposes several configuration options via Lambda environment variables. More detail on the available parameters can be found below. + +1. **spill_bucket** - When the data returned by your Lambda function exceeds Lambda’s limits, this is the bucket that the data will be written to for Athena to read the excess from. (e.g. my_bucket) +2. **spill_prefix** - (Optional) Defaults to sub-folder in your bucket called 'athena-federation-spill'. Used in conjunction with spill_bucket, this is the path within the above bucket that large responses are spilled to. You should configure an S3 lifecycle on this location to delete old spills after X days/Hours. +3. **kms_key_id** - (Optional) By default any data that is spilled to S3 is encrypted using AES-GCM and a randomly generated key. Setting a KMS Key ID allows your Lambda function to use KMS for key generation for a stronger source of encryption keys. (e.g. a7e63k4b-8loc-40db-a2a1-4d0en2cd8331) +4. **disable_spill_encryption** - (Optional) Defaults to False so that any data that is spilled to S3 is encrypted using AES-GMC either with a randomly generated key or using KMS to generate keys. Setting this to false will disable spill encryption. You may wish to disable this for improved performance, especially if your spill location in S3 uses S3 Server Side Encryption. (e.g. True or False) + +### Databases & Tables + +The Athena TPC-DS Connector generates a TPC-DS compliant database at one of four ("tpcds1", "tpcds10", "tpcds100", "tpcds250", "tpcds1000") scale factors. + +For a complete list of tables and columns please use `show tables` and `describe table` queries and a summary of tables below. You can find copies of TPC-DS queries that are compatible with this generated schema and data in the src/main/resources/queries directory of this module. + +1. call_center +1. catalog_page +1. catalog_returns +1. catalog_sales +1. customer +1. customer_address +1. customer_demographics +1. date_dim +1. dbgen_version +1. household_demographics +1. income_band +1. inventory +1. item +1. promotion +1. reason +1. ship_mode +1. store +1. store_returns +1. store_sales +1. time_dim +1. warehouse +1. web_page +1. web_returns +1. web_sales +1. web_site + +The below query is one example that is setup for use with a catalog called tpcds. + +```sql +SELECT + cd_gender, + cd_marital_status, + cd_education_status, + count(*) cnt1, + cd_purchase_estimate, + count(*) cnt2, + cd_credit_rating, + count(*) cnt3, + cd_dep_count, + count(*) cnt4, + cd_dep_employed_count, + count(*) cnt5, + cd_dep_college_count, + count(*) cnt6 +FROM + "lambda:tpcds".tpcds1.customer c, "lambda:tpcds".tpcds1.customer_address ca, "lambda:tpcds".tpcds1.customer_demographics +WHERE + c.c_current_addr_sk = ca.ca_address_sk AND + ca_county IN ('Rush County', 'Toole County', 'Jefferson County', + 'Dona Ana County', 'La Porte County') AND + cd_demo_sk = c.c_current_cdemo_sk AND + exists(SELECT * + FROM "lambda:tpcds".tpcds1.store_sales, "lambda:tpcds".tpcds1.date_dim + WHERE c.c_customer_sk = ss_customer_sk AND + ss_sold_date_sk = d_date_sk AND + d_year = 2002 AND + d_moy BETWEEN 1 AND 1 + 3) AND + (exists(SELECT * + FROM "lambda:tpcds".tpcds1.web_sales, "lambda:tpcds".tpcds1.date_dim + WHERE c.c_customer_sk = ws_bill_customer_sk AND + ws_sold_date_sk = d_date_sk AND + d_year = 2002 AND + d_moy BETWEEN 1 AND 1 + 3) OR + exists(SELECT * + FROM "lambda:tpcds".tpcds1.catalog_sales, "lambda:tpcds".tpcds1.date_dim + WHERE c.c_customer_sk = cs_ship_customer_sk AND + cs_sold_date_sk = d_date_sk AND + d_year = 2002 AND + d_moy BETWEEN 1 AND 1 + 3)) +GROUP BY cd_gender, + cd_marital_status, + cd_education_status, + cd_purchase_estimate, + cd_credit_rating, + cd_dep_count, + cd_dep_employed_count, + cd_dep_college_count +ORDER BY cd_gender, + cd_marital_status, + cd_education_status, + cd_purchase_estimate, + cd_credit_rating, + cd_dep_count, + cd_dep_employed_count, + cd_dep_college_count +LIMIT 100 +``` + +### Required Permissions + +Review the "Policies" section of the athena-tpcds.yaml file for full details on the IAM Policies required by this connector. A brief summary is below. + +1. S3 Write Access - In order to successfully handle large queries, the connector requires write access to a location in S3. +1. Athena GetQueryExecution - The connector uses this access to fast-fail when the upstream Athena query has terminated. + +### Deploying The Connector + +To use this connector in your queries, navigate to AWS Serverless Application Repository and deploy a pre-built version of this connector. Alternatively, you can build and deploy this connector from source follow the below steps or use the more detailed tutorial in the athena-example module: + +1. From the athena-federation-sdk dir, run `mvn clean install` if you haven't already. +2. From the athena-tpcds dir, run `mvn clean install`. +3. From the athena-tpcds dir, run `../tools/publish.sh S3_BUCKET_NAME athena-tpcds` to publish the connector to your private AWS Serverless Application Repository. The S3_BUCKET in the command is where a copy of the connector's code will be stored for Serverless Application Repository to retrieve it. This will allow users with permission to do so, the ability to deploy instances of the connector via 1-Click form. Then navigate to [Serverless Application Repository](https://aws.amazon.com/serverless/serverlessrepo) +4. Try running a query like the one below in Athena: +```sql +select * from "lambda:".schema.table limit 100 +``` + +## Performance + +The Athena tpcds Connector will attempt to parallelize queries based on the scale factor you have choosen. Predicate Pushdown is performed within the Lambda function. + +## License + +This project is licensed under the Apache-2.0 License. \ No newline at end of file diff --git a/athena-tpcds/athena-tpcds.yaml b/athena-tpcds/athena-tpcds.yaml new file mode 100644 index 0000000000..47d0bf071a --- /dev/null +++ b/athena-tpcds/athena-tpcds.yaml @@ -0,0 +1,64 @@ +Transform: 'AWS::Serverless-2016-10-31' +Metadata: + 'AWS::ServerlessRepo::Application': + Name: AthenaTPCDSConnector + Description: 'This connector enables Amazon Athena to communicate with a randomly generated TPC-DS data source.' + Author: 'Amazon Athena' + SpdxLicenseId: Apache-2.0 + LicenseUrl: LICENSE.txt + ReadmeUrl: README.md + Labels: + - athena-federation + HomePageUrl: 'https://github.com/awslabs/aws-athena-query-federation' + SemanticVersion: 1.0.0 + SourceCodeUrl: 'https://github.com/awslabs/aws-athena-query-federation' +Parameters: + AthenaCatalogName: + Description: 'The name you will give to this catalog in Athena. It will also be used as the function name.' + Type: String + SpillBucket: + Description: 'The bucket where this function can spill data.' + Type: String + SpillPrefix: + Description: 'The bucket prefix where this function can spill large responses.' + Type: String + Default: athena-spill + LambdaTimeout: + Description: 'Maximum Lambda invocation runtime in seconds. (min 1 - 900 max)' + Default: 900 + Type: Number + LambdaMemory: + Description: 'Lambda memory in MB (min 128 - 3008 max).' + Default: 3008 + Type: Number + DisableSpillEncryption: + Description: "WARNING: If set to 'true' encryption for spilled data is disabled." + Default: 'false' + Type: String +Resources: + ConnectorConfig: + Type: 'AWS::Serverless::Function' + Properties: + Environment: + Variables: + disable_spill_encryption: !Ref DisableSpillEncryption + spill_bucket: !Ref SpillBucket + spill_prefix: !Ref SpillPrefix + FunctionName: !Ref AthenaCatalogName + Handler: "com.amazonaws.athena.connectors.tpcds.TPCDSCompositeHandler" + CodeUri: "./target/athena-tpcds-1.0.jar" + Description: "This connector enables Amazon Athena to communicate with a randomly generated TPC-DS data source." + Runtime: java8 + Timeout: !Ref LambdaTimeout + MemorySize: !Ref LambdaMemory + Policies: + - Statement: + - Action: + - athena:GetQueryExecution + Effect: Allow + Resource: '*' + Version: '2012-10-17' + #S3CrudPolicy allows our connector to spill large responses to S3. You can optionally replace this pre-made policy + #with one that is more restrictive and can only 'put' but not read,delete, or overwrite files. + - S3CrudPolicy: + BucketName: !Ref SpillBucket \ No newline at end of file diff --git a/athena-tpcds/pom.xml b/athena-tpcds/pom.xml new file mode 100644 index 0000000000..129c923499 --- /dev/null +++ b/athena-tpcds/pom.xml @@ -0,0 +1,57 @@ + + + + aws-athena-query-federation + com.amazonaws + 1.0 + + 4.0.0 + + athena-tpcds + 1.0 + + + com.amazonaws + aws-athena-federation-sdk + ${aws-athena-federation-sdk.version} + + + com.teradata.tpcds + tpcds + 1.2 + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.1 + + false + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + package + + shade + + + + + + + \ No newline at end of file diff --git a/athena-tpcds/src/main/java/com/amazonaws/athena/connectors/tpcds/TPCDSCompositeHandler.java b/athena-tpcds/src/main/java/com/amazonaws/athena/connectors/tpcds/TPCDSCompositeHandler.java new file mode 100644 index 0000000000..6522776cfd --- /dev/null +++ b/athena-tpcds/src/main/java/com/amazonaws/athena/connectors/tpcds/TPCDSCompositeHandler.java @@ -0,0 +1,31 @@ +/*- + * #%L + * athena-tpcds + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.tpcds; + +import com.amazonaws.athena.connector.lambda.handlers.CompositeHandler; + +public class TPCDSCompositeHandler + extends CompositeHandler +{ + public TPCDSCompositeHandler() + { + super(new TPCDSMetadataHandler(), new TPCDSRecordHandler()); + } +} diff --git a/athena-tpcds/src/main/java/com/amazonaws/athena/connectors/tpcds/TPCDSMetadataHandler.java b/athena-tpcds/src/main/java/com/amazonaws/athena/connectors/tpcds/TPCDSMetadataHandler.java new file mode 100644 index 0000000000..ebd2d591cb --- /dev/null +++ b/athena-tpcds/src/main/java/com/amazonaws/athena/connectors/tpcds/TPCDSMetadataHandler.java @@ -0,0 +1,199 @@ +/*- + * #%L + * athena-tpcds + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.tpcds; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockWriter; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.handlers.MetadataHandler; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesResponse; +import com.amazonaws.athena.connector.lambda.security.EncryptionKeyFactory; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.google.common.collect.ImmutableSet; +import com.teradata.tpcds.Table; +import com.teradata.tpcds.column.Column; +import org.apache.arrow.util.VisibleForTesting; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Handles metadata requests for the Athena TPC-DS Connector. + *

+ * For more detail, please see the module's README.md, some notable characteristics of this class include: + *

+ * 1. Provides 5 Schems, each representing a different scale factor (1,10,100,250,1000) + * 2. Each schema has 25 TPC-DS tables + * 3. Each table is divided into NUM_SPLITS splits * scale_factor/10 + */ +public class TPCDSMetadataHandler + extends MetadataHandler +{ + private static final Logger logger = LoggerFactory.getLogger(TPCDSMetadataHandler.class); + + //The name of the field that contains the number of the split. This is used for parallelizing data generation. + protected static final String SPLIT_NUMBER_FIELD = "splitNum"; + //The name of the field that contains the total number of splits that were generated. + //This is used for parallelizing data generation. + protected static final String SPLIT_TOTAL_NUMBER_FIELD = "totalNumSplits"; + //The is the name of the field that contains the scale factor of the schema used in the request. + protected static final String SPLIT_SCALE_FACTOR_FIELD = "scaleFactor"; + //The list of valid schemas which also convey the scale factor + protected static final Set SCHEMA_NAMES = ImmutableSet.of("tpcds1", "tpcds10", "tpcds100", "tpcds250", "tpcds1000"); + + /** + * used to aid in debugging. Athena will use this name in conjunction with your catalog id + * to correlate relevant query errors. + */ + private static final String SOURCE_TYPE = "tpcds"; + + public TPCDSMetadataHandler() + { + super(SOURCE_TYPE); + } + + @VisibleForTesting + protected TPCDSMetadataHandler(EncryptionKeyFactory keyFactory, + AWSSecretsManager secretsManager, + AmazonAthena athena, + String spillBucket, + String spillPrefix) + { + super(keyFactory, secretsManager, athena, SOURCE_TYPE, spillBucket, spillPrefix); + } + + /** + * Returns our static list of schemas which correspond to the scale factor of the dataset we will generate. + * + * @see MetadataHandler + */ + @Override + public ListSchemasResponse doListSchemaNames(BlockAllocator allocator, ListSchemasRequest request) + { + logger.info("doListSchemaNames: enter - " + request); + return new ListSchemasResponse(request.getCatalogName(), SCHEMA_NAMES); + } + + /** + * Used to get the list of static tables from TerraData's TPCDS generator. + * + * @see MetadataHandler + */ + @Override + public ListTablesResponse doListTables(BlockAllocator allocator, ListTablesRequest request) + { + logger.info("doListTables: enter - " + request); + + List tables = Table.getBaseTables().stream() + .map(next -> new TableName(request.getSchemaName(), next.getName())) + .collect(Collectors.toList()); + + return new ListTablesResponse(request.getCatalogName(), tables); + } + + /** + * Used to get definition (field names, types, descriptions, etc...) of a Table using the static + * metadata provided by TerraData's TPCDS generator. + * + * @see MetadataHandler + */ + @Override + public GetTableResponse doGetTable(BlockAllocator allocator, GetTableRequest request) + { + logger.info("doGetTable: enter - " + request); + + Table table = TPCDSUtils.validateTable(request.getTableName()); + + SchemaBuilder schemaBuilder = SchemaBuilder.newBuilder(); + for (Column nextCol : table.getColumns()) { + schemaBuilder.addField(TPCDSUtils.convertColumn(nextCol)); + } + + return new GetTableResponse(request.getCatalogName(), + request.getTableName(), + schemaBuilder.build(), + Collections.EMPTY_SET); + } + + /** + * We do not support partitioning at this time since Partition Pruning Performance is not part of the dimensions + * we test using TPCDS. By making this a NoOp the Athena Federation SDK will automatically generate a single + * placeholder partition to signal to Athena that there is indeed data that needs to be read and that it should + * call get splits. + * + * @see MetadataHandler + */ + @Override + public void getPartitions(BlockWriter blockWriter, GetTableLayoutRequest request, QueryStatusChecker queryStatusChecker) + throws Exception + { + //NoOp + } + + /** + * Used to split-up the reads required to scan the requested batch of partition(s). We are generating a fixed + * number of splits based on the scale factor. + * + * @see MetadataHandler + */ + @Override + public GetSplitsResponse doGetSplits(BlockAllocator allocator, GetSplitsRequest request) + { + String catalogName = request.getCatalogName(); + int scaleFactor = TPCDSUtils.extractScaleFactor(request.getTableName().getSchemaName()); + int totalSplits = (int) Math.ceil(((double) scaleFactor / 48D)); //each split would be ~48MB + + logger.info("doGetSplits: Generating {} splits for {} at scale factor {}", + totalSplits, request.getTableName(), scaleFactor); + + int nextSplit = request.getContinuationToken() == null ? 0 : Integer.parseInt(request.getContinuationToken()); + Set splits = new HashSet<>(); + for (int i = nextSplit; i < totalSplits; i++) { + splits.add(Split.newBuilder(makeSpillLocation(request), makeEncryptionKey()) + .add(SPLIT_NUMBER_FIELD, String.valueOf(i)) + .add(SPLIT_TOTAL_NUMBER_FIELD, String.valueOf(totalSplits)) + .add(SPLIT_SCALE_FACTOR_FIELD, String.valueOf(scaleFactor)) + .build()); + if (splits.size() >= 1000) { + return new GetSplitsResponse(catalogName, splits, String.valueOf(i + 1)); + } + } + + logger.info("doGetSplits: exit - " + splits.size()); + return new GetSplitsResponse(catalogName, splits); + } +} diff --git a/athena-tpcds/src/main/java/com/amazonaws/athena/connectors/tpcds/TPCDSRecordHandler.java b/athena-tpcds/src/main/java/com/amazonaws/athena/connectors/tpcds/TPCDSRecordHandler.java new file mode 100644 index 0000000000..ffd0224e79 --- /dev/null +++ b/athena-tpcds/src/main/java/com/amazonaws/athena/connectors/tpcds/TPCDSRecordHandler.java @@ -0,0 +1,229 @@ +/*- + * #%L + * athena-tpcds + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.tpcds; + +import com.amazonaws.athena.connector.lambda.QueryStatusChecker; +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockSpiller; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.handlers.RecordHandler; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.athena.AmazonAthenaClientBuilder; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.amazonaws.services.secretsmanager.AWSSecretsManagerClientBuilder; +import com.teradata.tpcds.Results; +import com.teradata.tpcds.Session; +import com.teradata.tpcds.Table; +import com.teradata.tpcds.column.Column; +import com.teradata.tpcds.column.ColumnType; +import org.apache.arrow.util.VisibleForTesting; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.arrow.vector.types.pojo.Schema; +import org.joda.time.LocalDate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.math.BigDecimal; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import static com.amazonaws.athena.connectors.tpcds.TPCDSMetadataHandler.SPLIT_NUMBER_FIELD; +import static com.amazonaws.athena.connectors.tpcds.TPCDSMetadataHandler.SPLIT_SCALE_FACTOR_FIELD; +import static com.amazonaws.athena.connectors.tpcds.TPCDSMetadataHandler.SPLIT_TOTAL_NUMBER_FIELD; +import static com.teradata.tpcds.Results.constructResults; + +/** + * Handles data read record requests for the Athena TPC-DS Connector. + *

+ * For more detail, please see the module's README.md, some notable characteristics of this class include: + *

+ * 1. Generates data for the requested table on the fly. + * 2. Applies constraints to the data as it is generated, emulating predicate-pushdown. + */ +public class TPCDSRecordHandler + extends RecordHandler +{ + private static final Logger logger = LoggerFactory.getLogger(TPCDSRecordHandler.class); + + /** + * used to aid in debugging. Athena will use this name in conjunction with your catalog id + * to correlate relevant query errors. + */ + private static final String SOURCE_TYPE = "tpcds"; + + public TPCDSRecordHandler() + { + super(AmazonS3ClientBuilder.defaultClient(), AWSSecretsManagerClientBuilder.defaultClient(), AmazonAthenaClientBuilder.defaultClient(), SOURCE_TYPE); + } + + @VisibleForTesting + protected TPCDSRecordHandler(AmazonS3 amazonS3, AWSSecretsManager secretsManager, AmazonAthena athena) + { + super(amazonS3, secretsManager, athena, SOURCE_TYPE); + } + + /** + * Generated TPCDS data for the given Table and scale factor as defined by the requested Split. + * + * @see RecordHandler + */ + @Override + protected void readWithConstraint(BlockSpiller spiller, ReadRecordsRequest recordsRequest, QueryStatusChecker queryStatusChecker) + throws IOException + { + Split split = recordsRequest.getSplit(); + int splitNumber = Integer.parseInt(split.getProperty(SPLIT_NUMBER_FIELD)); + int totalNumSplits = Integer.parseInt(split.getProperty(SPLIT_TOTAL_NUMBER_FIELD)); + int scaleFactor = Integer.parseInt(split.getProperty(SPLIT_SCALE_FACTOR_FIELD)); + Table table = validateTable(recordsRequest.getTableName()); + + Session session = Session.getDefaultSession() + .withScale(scaleFactor) + .withParallelism(totalNumSplits) + .withChunkNumber(splitNumber + 1) + .withTable(table) + .withNoSexism(true); + + Results results = constructResults(table, session); + Iterator>> itr = results.iterator(); + + Map writers = makeWriters(recordsRequest.getSchema(), table); + while (itr.hasNext() && queryStatusChecker.isQueryRunning()) { + List row = itr.next().get(0); + spiller.writeRows((Block block, int numRow) -> { + boolean matched = true; + for (Map.Entry nextWriter : writers.entrySet()) { + matched &= nextWriter.getValue().write(block, numRow, row.get(nextWriter.getKey())); + } + return matched ? 1 : 0; + }); + } + } + + /** + * Required that the requested Table be present in the TPCDS generated schema. + * + * @param tableName The fully qualified name of the requested table. + * @return The TPCDS table, if present, otherwise the method throws. + */ + private Table validateTable(TableName tableName) + { + Optional

table = Table.getBaseTables().stream() + .filter(next -> next.getName().equals(tableName.getTableName())) + .findFirst(); + + if (!table.isPresent()) { + throw new RuntimeException("Unknown table " + tableName); + } + + return table.get(); + } + + /** + * Generates the CellWriters used to convert the TPCDS Generators data to Apache Arrow. + * + * @param schemaForRead The schema to read/project. + * @param table The TPCDS Table we are reading from. + * @return Map where integer is the Column position in the TPCDS data set and the CellWriter + * can be used to read,convert,write the value at that position for any row into the correct position and type + * in our Apache Arrow response. + */ + private Map makeWriters(Schema schemaForRead, Table table) + { + Map columnPositions = new HashMap<>(); + for (Column next : table.getColumns()) { + columnPositions.put(next.getName(), next); + } + + //We use this approach to reduce the overhead of field lookups. This isn't as good as true columnar processing + //using Arrow but it gets us ~80% of the way there from a rows/second per cpu-cycle perspective. + Map writers = new HashMap<>(); + for (Field nextField : schemaForRead.getFields()) { + Column column = columnPositions.get(nextField.getName()); + writers.put(column.getPosition(), makeWriter(nextField, column)); + } + return writers; + } + + /** + * Makes a CellWriter for the provided Apache Arrow Field and TPCDS Column. + * + * @param field The Apache Arrow Field. + * @param column The corresponding TPCDS Column. + * @return The CellWriter that can be used to convert and write values for the provided Field/Column pair. + */ + private CellWriter makeWriter(Field field, Column column) + { + ColumnType type = column.getType(); + switch (type.getBase()) { + case TIME: + case IDENTIFIER: + return (Block block, int rowNum, String rawValue) -> { + Long value = (rawValue != null) ? Long.parseLong(rawValue) : null; + return block.setValue(field.getName(), rowNum, value); + }; + case INTEGER: + return (Block block, int rowNum, String rawValue) -> { + Integer value = (rawValue != null) ? Integer.parseInt(rawValue) : null; + return block.setValue(field.getName(), rowNum, value); + }; + case DATE: + return (Block block, int rowNum, String rawValue) -> { + Date value = (rawValue != null) ? LocalDate.parse(rawValue).toDate() : null; + return block.setValue(field.getName(), rowNum, value); + }; + case DECIMAL: + return (Block block, int rowNum, String rawValue) -> { + BigDecimal value = (rawValue != null) ? new BigDecimal(rawValue) : null; + return block.setValue(field.getName(), rowNum, value); + }; + case CHAR: + case VARCHAR: + return (Block block, int rowNum, String rawValue) -> { + return block.setValue(field.getName(), rowNum, rawValue); + }; + } + throw new IllegalArgumentException("Unsupported TPC-DS type " + column.getName() + ":" + column.getType().getBase()); + } + + public interface CellWriter + { + /** + * Converts a value from TPCDS' string representation into the appropriate Apache Arrow type + * and writes it to the correct field in the provided Block and row. The implementation should + * also apply constraints as an optimization. + * + * @param block The Apache Arrow Block to write into. + * @param rowNum The row number in the Arrow Block to write into. + * @param value The value to convert and write into the Apache Arrow Block. + * @return True if the value passed all Contraints. + */ + boolean write(Block block, int rowNum, String value); + } +} diff --git a/athena-tpcds/src/main/java/com/amazonaws/athena/connectors/tpcds/TPCDSUtils.java b/athena-tpcds/src/main/java/com/amazonaws/athena/connectors/tpcds/TPCDSUtils.java new file mode 100644 index 0000000000..065d3b4a4c --- /dev/null +++ b/athena-tpcds/src/main/java/com/amazonaws/athena/connectors/tpcds/TPCDSUtils.java @@ -0,0 +1,106 @@ +/*- + * #%L + * athena-tpcds + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.tpcds; + +import com.amazonaws.athena.connector.lambda.data.FieldBuilder; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.teradata.tpcds.Table; +import com.teradata.tpcds.column.Column; +import com.teradata.tpcds.column.ColumnType; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.arrow.vector.types.pojo.Field; + +import java.util.Optional; + +/** + * Utility class that centralizes a few commonly used tools for working with + * the TPC-DS Tables,Columns,Schemas. + */ +public class TPCDSUtils +{ + private TPCDSUtils() {} + + /** + * Converts from TPCDS columns to Apache Arrow fields. + * + * @param column The TPCDS column to conver. + * @return The Apache Arrow field that corresponds to the TPCDS column. + */ + public static Field convertColumn(Column column) + { + ColumnType type = column.getType(); + switch (type.getBase()) { + case TIME: + case IDENTIFIER: + return FieldBuilder.newBuilder(column.getName(), Types.MinorType.BIGINT.getType()).build(); + case INTEGER: + return FieldBuilder.newBuilder(column.getName(), Types.MinorType.INT.getType()).build(); + case DATE: + return FieldBuilder.newBuilder(column.getName(), Types.MinorType.DATEDAY.getType()).build(); + case DECIMAL: + ArrowType arrowType = new ArrowType.Decimal(type.getPrecision().get(), type.getScale().get()); + return FieldBuilder.newBuilder(column.getName(), arrowType).build(); + case CHAR: + case VARCHAR: + return FieldBuilder.newBuilder(column.getName(), Types.MinorType.VARCHAR.getType()).build(); + } + throw new IllegalArgumentException("Unsupported TPC-DS type " + column.getName() + ":" + column.getType().getBase()); + } + + /** + * Extracts the scale factor of the schema from its name. + * + * @param schemaName The schema name from which to extract a scale factor. + * @return The scale factor associated with the schema name. Method throws is the scale factor can not be determined. + */ + public static int extractScaleFactor(String schemaName) + { + if (!schemaName.startsWith("tpcds")) { + throw new RuntimeException("Unknown schema format " + schemaName + ", can not extract scale factor."); + } + + try { + return Integer.parseInt(schemaName.substring(5)); + } + catch (RuntimeException ex) { + throw new RuntimeException("Unknown schema format " + schemaName + ", can not extract scale factor.", ex); + } + } + + /** + * Required that the requested Table be present in the TPCDS generated schema. + * + * @param tableName The fully qualified name of the requested table. + * @return The TPCDS table, if present, otherwise the method throws. + */ + public static Table validateTable(TableName tableName) + { + Optional
table = Table.getBaseTables().stream() + .filter(next -> next.getName().equals(tableName.getTableName())) + .findFirst(); + + if (!table.isPresent()) { + throw new RuntimeException("Unknown table " + tableName); + } + + return table.get(); + } +} diff --git a/athena-tpcds/src/main/resources/queries/q1.sql b/athena-tpcds/src/main/resources/queries/q1.sql new file mode 100644 index 0000000000..e10b68cdc2 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q1.sql @@ -0,0 +1,19 @@ +WITH customer_total_return AS +( SELECT + sr_customer_sk AS ctr_customer_sk, + sr_store_sk AS ctr_store_sk, + sum(sr_return_amt) AS ctr_total_return + FROM store_returns[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ] + WHERE sr_returned_date_sk = d_date_sk AND d_year = 2000 + GROUP BY sr_customer_sk, sr_store_sk) +SELECT c_customer_id +FROM customer_total_return ctr1, store[ TABLE_SUFFIX ], customer[ TABLE_SUFFIX ] +WHERE ctr1.ctr_total_return > + (SELECT avg(ctr_total_return) * 1.2 + FROM customer_total_return ctr2 + WHERE ctr1.ctr_store_sk = ctr2.ctr_store_sk) + AND s_store_sk = ctr1.ctr_store_sk + AND s_state = 'TN' + AND ctr1.ctr_customer_sk = c_customer_sk +ORDER BY c_customer_id +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q10.sql b/athena-tpcds/src/main/resources/queries/q10.sql new file mode 100644 index 0000000000..0adab7ebd5 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q10.sql @@ -0,0 +1,57 @@ +SELECT + cd_gender, + cd_marital_status, + cd_education_status, + count(*) cnt1, + cd_purchase_estimate, + count(*) cnt2, + cd_credit_rating, + count(*) cnt3, + cd_dep_count, + count(*) cnt4, + cd_dep_employed_count, + count(*) cnt5, + cd_dep_college_count, + count(*) cnt6 +FROM + customer[ TABLE_SUFFIX ] c, customer_address[ TABLE_SUFFIX ] ca, customer_demographics[ TABLE_SUFFIX ] +WHERE + c.c_current_addr_sk = ca.ca_address_sk AND + ca_county IN ('Rush County', 'Toole County', 'Jefferson County', + 'Dona Ana County', 'La Porte County') AND + cd_demo_sk = c.c_current_cdemo_sk AND + exists(SELECT * + FROM store_sales[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ] + WHERE c.c_customer_sk = ss_customer_sk AND + ss_sold_date_sk = d_date_sk AND + d_year = 2002 AND + d_moy BETWEEN 1 AND 1 + 3) AND + (exists(SELECT * + FROM web_sales[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ] + WHERE c.c_customer_sk = ws_bill_customer_sk AND + ws_sold_date_sk = d_date_sk AND + d_year = 2002 AND + d_moy BETWEEN 1 AND 1 + 3) OR + exists(SELECT * + FROM catalog_sales[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ] + WHERE c.c_customer_sk = cs_ship_customer_sk AND + cs_sold_date_sk = d_date_sk AND + d_year = 2002 AND + d_moy BETWEEN 1 AND 1 + 3)) +GROUP BY cd_gender, + cd_marital_status, + cd_education_status, + cd_purchase_estimate, + cd_credit_rating, + cd_dep_count, + cd_dep_employed_count, + cd_dep_college_count +ORDER BY cd_gender, + cd_marital_status, + cd_education_status, + cd_purchase_estimate, + cd_credit_rating, + cd_dep_count, + cd_dep_employed_count, + cd_dep_college_count +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q11.sql b/athena-tpcds/src/main/resources/queries/q11.sql new file mode 100644 index 0000000000..8ceac01f4f --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q11.sql @@ -0,0 +1,68 @@ +WITH year_total AS ( + SELECT + c_customer_id customer_id, + c_first_name customer_first_name, + c_last_name customer_last_name, + c_preferred_cust_flag customer_preferred_cust_flag, + c_birth_country customer_birth_country, + c_login customer_login, + c_email_address customer_email_address, + d_year dyear, + sum(ss_ext_list_price - ss_ext_discount_amt) year_total, + 's' sale_type + FROM customer[ TABLE_SUFFIX ], store_sales[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ] + WHERE c_customer_sk = ss_customer_sk + AND ss_sold_date_sk = d_date_sk + GROUP BY c_customer_id + , c_first_name + , c_last_name + , d_year + , c_preferred_cust_flag + , c_birth_country + , c_login + , c_email_address + , d_year + UNION ALL + SELECT + c_customer_id customer_id, + c_first_name customer_first_name, + c_last_name customer_last_name, + c_preferred_cust_flag customer_preferred_cust_flag, + c_birth_country customer_birth_country, + c_login customer_login, + c_email_address customer_email_address, + d_year dyear, + sum(ws_ext_list_price - ws_ext_discount_amt) year_total, + 'w' sale_type + FROM customer[ TABLE_SUFFIX ], web_sales[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ] + WHERE c_customer_sk = ws_bill_customer_sk + AND ws_sold_date_sk = d_date_sk + GROUP BY + c_customer_id, c_first_name, c_last_name, c_preferred_cust_flag, c_birth_country, + c_login, c_email_address, d_year) +SELECT t_s_secyear.customer_preferred_cust_flag +FROM year_total t_s_firstyear + , year_total t_s_secyear + , year_total t_w_firstyear + , year_total t_w_secyear +WHERE t_s_secyear.customer_id = t_s_firstyear.customer_id + AND t_s_firstyear.customer_id = t_w_secyear.customer_id + AND t_s_firstyear.customer_id = t_w_firstyear.customer_id + AND t_s_firstyear.sale_type = 's' + AND t_w_firstyear.sale_type = 'w' + AND t_s_secyear.sale_type = 's' + AND t_w_secyear.sale_type = 'w' + AND t_s_firstyear.dyear = 2001 + AND t_s_secyear.dyear = 2001 + 1 + AND t_w_firstyear.dyear = 2001 + AND t_w_secyear.dyear = 2001 + 1 + AND t_s_firstyear.year_total > 0 + AND t_w_firstyear.year_total > 0 + AND CASE WHEN t_w_firstyear.year_total > 0 + THEN t_w_secyear.year_total / t_w_firstyear.year_total + ELSE NULL END + > CASE WHEN t_s_firstyear.year_total > 0 + THEN t_s_secyear.year_total / t_s_firstyear.year_total + ELSE NULL END +ORDER BY t_s_secyear.customer_preferred_cust_flag +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q12.sql b/athena-tpcds/src/main/resources/queries/q12.sql new file mode 100644 index 0000000000..c1528a206d --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q12.sql @@ -0,0 +1,22 @@ +SELECT + i_item_desc, + i_category, + i_class, + i_current_price, + sum(ws_ext_sales_price) AS itemrevenue, + sum(ws_ext_sales_price) * 100 / sum(sum(ws_ext_sales_price)) + OVER + (PARTITION BY i_class) AS revenueratio +FROM + web_sales[ TABLE_SUFFIX ], item[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ] +WHERE + ws_item_sk = i_item_sk + AND i_category IN ('Sports', 'Books', 'Home') + AND ws_sold_date_sk = d_date_sk + AND CAST(d_date as DATE) BETWEEN cast('1999-02-22' AS DATE) + AND (cast('1999-02-22' AS DATE) + INTERVAL '30' day) +GROUP BY + i_item_id, i_item_desc, i_category, i_class, i_current_price +ORDER BY + i_category, i_class, i_item_id, i_item_desc, revenueratio +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q13.sql b/athena-tpcds/src/main/resources/queries/q13.sql new file mode 100644 index 0000000000..1d3fe9721c --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q13.sql @@ -0,0 +1,49 @@ +SELECT + avg(ss_quantity), + avg(ss_ext_sales_price), + avg(ss_ext_wholesale_cost), + sum(ss_ext_wholesale_cost) +FROM store_sales[ TABLE_SUFFIX ] + , store[ TABLE_SUFFIX ] + , customer_demographics[ TABLE_SUFFIX ] + , household_demographics[ TABLE_SUFFIX ] + , customer_address[ TABLE_SUFFIX ] + , date_dim[ TABLE_SUFFIX ] +WHERE s_store_sk = ss_store_sk + AND ss_sold_date_sk = d_date_sk AND d_year = 2001 + AND ((ss_hdemo_sk = hd_demo_sk + AND cd_demo_sk = ss_cdemo_sk + AND cd_marital_status = 'M' + AND cd_education_status = 'Advanced Degree' + AND ss_sales_price BETWEEN 100.00 AND 150.00 + AND hd_dep_count = 3 +) OR + (ss_hdemo_sk = hd_demo_sk + AND cd_demo_sk = ss_cdemo_sk + AND cd_marital_status = 'S' + AND cd_education_status = 'College' + AND ss_sales_price BETWEEN 50.00 AND 100.00 + AND hd_dep_count = 1 + ) OR + (ss_hdemo_sk = hd_demo_sk + AND cd_demo_sk = ss_cdemo_sk + AND cd_marital_status = 'W' + AND cd_education_status = '2 yr Degree' + AND ss_sales_price BETWEEN 150.00 AND 200.00 + AND hd_dep_count = 1 + )) + AND ((ss_addr_sk = ca_address_sk + AND ca_country = 'United States' + AND ca_state IN ('TX', 'OH', 'TX') + AND ss_net_profit BETWEEN 100 AND 200 +) OR + (ss_addr_sk = ca_address_sk + AND ca_country = 'United States' + AND ca_state IN ('OR', 'NM', 'KY') + AND ss_net_profit BETWEEN 150 AND 300 + ) OR + (ss_addr_sk = ca_address_sk + AND ca_country = 'United States' + AND ca_state IN ('VA', 'TX', 'MS') + AND ss_net_profit BETWEEN 50 AND 250 + )) diff --git a/athena-tpcds/src/main/resources/queries/q14a.sql b/athena-tpcds/src/main/resources/queries/q14a.sql new file mode 100644 index 0000000000..5293b6054e --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q14a.sql @@ -0,0 +1,120 @@ +WITH cross_items AS +(SELECT i_item_sk ss_item_sk + FROM item[ TABLE_SUFFIX ], + (SELECT + iss.i_brand_id brand_id, + iss.i_class_id class_id, + iss.i_category_id category_id + FROM store_sales[ TABLE_SUFFIX ], item[ TABLE_SUFFIX ] iss, date_dim[ TABLE_SUFFIX ] d1 + WHERE ss_item_sk = iss.i_item_sk + AND ss_sold_date_sk = d1.d_date_sk + AND d1.d_year BETWEEN 1999 AND 1999 + 2 + INTERSECT + SELECT + ics.i_brand_id, + ics.i_class_id, + ics.i_category_id + FROM catalog_sales[ TABLE_SUFFIX ], item[ TABLE_SUFFIX ] ics, date_dim[ TABLE_SUFFIX ] d2 + WHERE cs_item_sk = ics.i_item_sk + AND cs_sold_date_sk = d2.d_date_sk + AND d2.d_year BETWEEN 1999 AND 1999 + 2 + INTERSECT + SELECT + iws.i_brand_id, + iws.i_class_id, + iws.i_category_id + FROM web_sales[ TABLE_SUFFIX ], item[ TABLE_SUFFIX ] iws, date_dim[ TABLE_SUFFIX ] d3 + WHERE ws_item_sk = iws.i_item_sk + AND ws_sold_date_sk = d3.d_date_sk + AND d3.d_year BETWEEN 1999 AND 1999 + 2) x + WHERE i_brand_id = brand_id + AND i_class_id = class_id + AND i_category_id = category_id +), + avg_sales AS + (SELECT avg(quantity * list_price) average_sales + FROM ( + SELECT + ss_quantity quantity, + ss_list_price list_price + FROM store_sales[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ] + WHERE ss_sold_date_sk = d_date_sk + AND d_year BETWEEN 1999 AND 2001 + UNION ALL + SELECT + cs_quantity quantity, + cs_list_price list_price + FROM catalog_sales[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ] + WHERE cs_sold_date_sk = d_date_sk + AND d_year BETWEEN 1999 AND 1999 + 2 + UNION ALL + SELECT + ws_quantity quantity, + ws_list_price list_price + FROM web_sales[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ] + WHERE ws_sold_date_sk = d_date_sk + AND d_year BETWEEN 1999 AND 1999 + 2) x) +SELECT + channel, + i_brand_id, + i_class_id, + i_category_id, + sum(sales), + sum(number_sales) +FROM ( + SELECT + 'store' channel, + i_brand_id, + i_class_id, + i_category_id, + sum(ss_quantity * ss_list_price) sales, + count(*) number_sales + FROM store_sales[ TABLE_SUFFIX ], item[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ] + WHERE ss_item_sk IN (SELECT ss_item_sk + FROM cross_items) + AND ss_item_sk = i_item_sk + AND ss_sold_date_sk = d_date_sk + AND d_year = 1999 + 2 + AND d_moy = 11 + GROUP BY i_brand_id, i_class_id, i_category_id + HAVING sum(ss_quantity * ss_list_price) > (SELECT average_sales + FROM avg_sales) + UNION ALL + SELECT + 'catalog' channel, + i_brand_id, + i_class_id, + i_category_id, + sum(cs_quantity * cs_list_price) sales, + count(*) number_sales + FROM catalog_sales[ TABLE_SUFFIX ], item[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ] + WHERE cs_item_sk IN (SELECT ss_item_sk + FROM cross_items) + AND cs_item_sk = i_item_sk + AND cs_sold_date_sk = d_date_sk + AND d_year = 1999 + 2 + AND d_moy = 11 + GROUP BY i_brand_id, i_class_id, i_category_id + HAVING sum(cs_quantity * cs_list_price) > (SELECT average_sales FROM avg_sales) + UNION ALL + SELECT + 'web' channel, + i_brand_id, + i_class_id, + i_category_id, + sum(ws_quantity * ws_list_price) sales, + count(*) number_sales + FROM web_sales[ TABLE_SUFFIX ], item[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ] + WHERE ws_item_sk IN (SELECT ss_item_sk + FROM cross_items) + AND ws_item_sk = i_item_sk + AND ws_sold_date_sk = d_date_sk + AND d_year = 1999 + 2 + AND d_moy = 11 + GROUP BY i_brand_id, i_class_id, i_category_id + HAVING sum(ws_quantity * ws_list_price) > (SELECT average_sales + FROM avg_sales) + ) +GROUP BY ROLLUP (channel, i_brand_id, i_class_id, i_category_id) +ORDER BY channel, i_brand_id, i_class_id, i_category_id +LIMIT 100 \ No newline at end of file diff --git a/athena-tpcds/src/main/resources/queries/q14b.sql b/athena-tpcds/src/main/resources/queries/q14b.sql new file mode 100644 index 0000000000..929a8484bf --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q14b.sql @@ -0,0 +1,95 @@ +WITH cross_items AS +(SELECT i_item_sk ss_item_sk + FROM item, + (SELECT + iss.i_brand_id brand_id, + iss.i_class_id class_id, + iss.i_category_id category_id + FROM store_sales, item iss, date_dim d1 + WHERE ss_item_sk = iss.i_item_sk + AND ss_sold_date_sk = d1.d_date_sk + AND d1.d_year BETWEEN 1999 AND 1999 + 2 + INTERSECT + SELECT + ics.i_brand_id, + ics.i_class_id, + ics.i_category_id + FROM catalog_sales, item ics, date_dim d2 + WHERE cs_item_sk = ics.i_item_sk + AND cs_sold_date_sk = d2.d_date_sk + AND d2.d_year BETWEEN 1999 AND 1999 + 2 + INTERSECT + SELECT + iws.i_brand_id, + iws.i_class_id, + iws.i_category_id + FROM web_sales, item iws, date_dim d3 + WHERE ws_item_sk = iws.i_item_sk + AND ws_sold_date_sk = d3.d_date_sk + AND d3.d_year BETWEEN 1999 AND 1999 + 2) x + WHERE i_brand_id = brand_id + AND i_class_id = class_id + AND i_category_id = category_id +), + avg_sales AS + (SELECT avg(quantity * list_price) average_sales + FROM (SELECT + ss_quantity quantity, + ss_list_price list_price + FROM store_sales, date_dim + WHERE ss_sold_date_sk = d_date_sk AND d_year BETWEEN 1999 AND 1999 + 2 + UNION ALL + SELECT + cs_quantity quantity, + cs_list_price list_price + FROM catalog_sales, date_dim + WHERE cs_sold_date_sk = d_date_sk AND d_year BETWEEN 1999 AND 1999 + 2 + UNION ALL + SELECT + ws_quantity quantity, + ws_list_price list_price + FROM web_sales, date_dim + WHERE ws_sold_date_sk = d_date_sk AND d_year BETWEEN 1999 AND 1999 + 2) x) +SELECT * +FROM + (SELECT + 'store' channel, + i_brand_id, + i_class_id, + i_category_id, + sum(ss_quantity * ss_list_price) sales, + count(*) number_sales + FROM store_sales, item, date_dim + WHERE ss_item_sk IN (SELECT ss_item_sk + FROM cross_items) + AND ss_item_sk = i_item_sk + AND ss_sold_date_sk = d_date_sk + AND d_week_seq = (SELECT d_week_seq + FROM date_dim + WHERE d_year = 1999 + 1 AND d_moy = 12 AND d_dom = 11) + GROUP BY i_brand_id, i_class_id, i_category_id + HAVING sum(ss_quantity * ss_list_price) > (SELECT average_sales + FROM avg_sales)) this_year, + (SELECT + 'store' channel, + i_brand_id, + i_class_id, + i_category_id, + sum(ss_quantity * ss_list_price) sales, + count(*) number_sales + FROM store_sales, item, date_dim + WHERE ss_item_sk IN (SELECT ss_item_sk + FROM cross_items) + AND ss_item_sk = i_item_sk + AND ss_sold_date_sk = d_date_sk + AND d_week_seq = (SELECT d_week_seq + FROM date_dim + WHERE d_year = 1999 AND d_moy = 12 AND d_dom = 11) + GROUP BY i_brand_id, i_class_id, i_category_id + HAVING sum(ss_quantity * ss_list_price) > (SELECT average_sales + FROM avg_sales)) last_year +WHERE this_year.i_brand_id = last_year.i_brand_id + AND this_year.i_class_id = last_year.i_class_id + AND this_year.i_category_id = last_year.i_category_id +ORDER BY this_year.channel, this_year.i_brand_id, this_year.i_class_id, this_year.i_category_id +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q15.sql b/athena-tpcds/src/main/resources/queries/q15.sql new file mode 100644 index 0000000000..bdd125ab01 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q15.sql @@ -0,0 +1,15 @@ +SELECT + ca_zip, + sum(cs_sales_price) +FROM catalog_sales[ TABLE_SUFFIX ], customer[ TABLE_SUFFIX ], customer_address[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ] +WHERE cs_bill_customer_sk = c_customer_sk + AND c_current_addr_sk = ca_address_sk + AND (substr(ca_zip, 1, 5) IN ('85669', '86197', '88274', '83405', '86475', + '85392', '85460', '80348', '81792') + OR ca_state IN ('CA', 'WA', 'GA') + OR cs_sales_price > 500) + AND cs_sold_date_sk = d_date_sk + AND d_qoy = 2 AND d_year = 2001 +GROUP BY ca_zip +ORDER BY ca_zip +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q16.sql b/athena-tpcds/src/main/resources/queries/q16.sql new file mode 100644 index 0000000000..cf6636cb44 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q16.sql @@ -0,0 +1,23 @@ +SELECT + count(DISTINCT cs_order_number) AS "order count ", + sum(cs_ext_ship_cost) AS "total shipping cost ", + sum(cs_net_profit) AS "total net profit " +FROM + catalog_sales cs1, date_dim, customer_address, call_center +WHERE + CAST(d_date as DATE) BETWEEN CAST('2002-02-01' AS DATE) AND (CAST('2002-02-01' AS DATE) + INTERVAL '60' day) + AND cs1.cs_ship_date_sk = d_date_sk + AND cs1.cs_ship_addr_sk = ca_address_sk + AND ca_state = 'GA' + AND cs1.cs_call_center_sk = cc_call_center_sk + AND cc_county IN + ('Williamson County', 'Williamson County', 'Williamson County', 'Williamson County', 'Williamson County') + AND EXISTS(SELECT * + FROM catalog_sales cs2 + WHERE cs1.cs_order_number = cs2.cs_order_number + AND cs1.cs_warehouse_sk <> cs2.cs_warehouse_sk) + AND NOT EXISTS(SELECT * + FROM catalog_returns cr1 + WHERE cs1.cs_order_number = cr1.cr_order_number) +ORDER BY count(DISTINCT cs_order_number) +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q17.sql b/athena-tpcds/src/main/resources/queries/q17.sql new file mode 100644 index 0000000000..cecf05fc46 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q17.sql @@ -0,0 +1,33 @@ +SELECT + i_item_id, + i_item_desc, + s_state, + count(ss_quantity) AS store_sales_quantitycount, + avg(ss_quantity) AS store_sales_quantityave, + stddev_samp(ss_quantity) AS store_sales_quantitystdev, + stddev_samp(ss_quantity) / avg(ss_quantity) AS store_sales_quantitycov, + count(sr_return_quantity) as_store_returns_quantitycount, + avg(sr_return_quantity) as_store_returns_quantityave, + stddev_samp(sr_return_quantity) as_store_returns_quantitystdev, + stddev_samp(sr_return_quantity) / avg(sr_return_quantity) AS store_returns_quantitycov, + count(cs_quantity) AS catalog_sales_quantitycount, + avg(cs_quantity) AS catalog_sales_quantityave, + stddev_samp(cs_quantity) / avg(cs_quantity) AS catalog_sales_quantitystdev, + stddev_samp(cs_quantity) / avg(cs_quantity) AS catalog_sales_quantitycov +FROM store_sales[ TABLE_SUFFIX ], store_returns[ TABLE_SUFFIX ], catalog_sales[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ] d1, date_dim[ TABLE_SUFFIX ] d2, date_dim[ TABLE_SUFFIX ] d3, store[ TABLE_SUFFIX ], item[ TABLE_SUFFIX ] +WHERE d1.d_quarter_name = '2001Q1' + AND d1.d_date_sk = ss_sold_date_sk + AND i_item_sk = ss_item_sk + AND s_store_sk = ss_store_sk + AND ss_customer_sk = sr_customer_sk + AND ss_item_sk = sr_item_sk + AND ss_ticket_number = sr_ticket_number + AND sr_returned_date_sk = d2.d_date_sk + AND d2.d_quarter_name IN ('2001Q1', '2001Q2', '2001Q3') + AND sr_customer_sk = cs_bill_customer_sk + AND sr_item_sk = cs_item_sk + AND cs_sold_date_sk = d3.d_date_sk + AND d3.d_quarter_name IN ('2001Q1', '2001Q2', '2001Q3') +GROUP BY i_item_id, i_item_desc, s_state +ORDER BY i_item_id, i_item_desc, s_state +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q18.sql b/athena-tpcds/src/main/resources/queries/q18.sql new file mode 100644 index 0000000000..91d5b489fa --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q18.sql @@ -0,0 +1,28 @@ +SELECT + i_item_id, + ca_country, + ca_state, + ca_county, + avg(cast(cs_quantity AS DECIMAL(12, 2))) agg1, + avg(cast(cs_list_price AS DECIMAL(12, 2))) agg2, + avg(cast(cs_coupon_amt AS DECIMAL(12, 2))) agg3, + avg(cast(cs_sales_price AS DECIMAL(12, 2))) agg4, + avg(cast(cs_net_profit AS DECIMAL(12, 2))) agg5, + avg(cast(c_birth_year AS DECIMAL(12, 2))) agg6, + avg(cast(cd1.cd_dep_count AS DECIMAL(12, 2))) agg7 +FROM catalog_sales[ TABLE_SUFFIX ], customer_demographics[ TABLE_SUFFIX ] cd1, + customer_demographics[ TABLE_SUFFIX ] cd2, customer[ TABLE_SUFFIX ], customer_address[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ], item[ TABLE_SUFFIX ] +WHERE cs_sold_date_sk = d_date_sk AND + cs_item_sk = i_item_sk AND + cs_bill_cdemo_sk = cd1.cd_demo_sk AND + cs_bill_customer_sk = c_customer_sk AND + cd1.cd_gender = 'F' AND + cd1.cd_education_status = 'Unknown' AND + c_current_cdemo_sk = cd2.cd_demo_sk AND + c_current_addr_sk = ca_address_sk AND + c_birth_month IN (1, 6, 8, 9, 12, 2) AND + d_year = 1998 AND + ca_state IN ('MS', 'IN', 'ND', 'OK', 'NM', 'VA', 'MS') +GROUP BY ROLLUP (i_item_id, ca_country, ca_state, ca_county) +ORDER BY ca_country, ca_state, ca_county, i_item_id +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q19.sql b/athena-tpcds/src/main/resources/queries/q19.sql new file mode 100644 index 0000000000..63579caf38 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q19.sql @@ -0,0 +1,19 @@ +SELECT + i_brand_id brand_id, + i_brand brand, + i_manufact_id, + i_manufact, + sum(ss_ext_sales_price) ext_price +FROM date_dim[ TABLE_SUFFIX ], store_sales[ TABLE_SUFFIX ], item[ TABLE_SUFFIX ], customer[ TABLE_SUFFIX ], customer_address[ TABLE_SUFFIX ], store[ TABLE_SUFFIX ] +WHERE d_date_sk = ss_sold_date_sk + AND ss_item_sk = i_item_sk + AND i_manager_id = 8 + AND d_moy = 11 + AND d_year = 1998 + AND ss_customer_sk = c_customer_sk + AND c_current_addr_sk = ca_address_sk + AND substr(ca_zip, 1, 5) <> substr(s_zip, 1, 5) + AND ss_store_sk = s_store_sk +GROUP BY i_brand, i_brand_id, i_manufact_id, i_manufact +ORDER BY ext_price DESC, brand, brand_id, i_manufact_id, i_manufact +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q2.sql b/athena-tpcds/src/main/resources/queries/q2.sql new file mode 100644 index 0000000000..b722db6b58 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q2.sql @@ -0,0 +1,81 @@ +WITH wscs AS +( SELECT + sold_date_sk, + sales_price + FROM (SELECT + ws_sold_date_sk sold_date_sk, + ws_ext_sales_price sales_price + FROM web_sales[ TABLE_SUFFIX ]) x + UNION ALL + (SELECT + cs_sold_date_sk sold_date_sk, + cs_ext_sales_price sales_price + FROM catalog_sales[ TABLE_SUFFIX ])), + wswscs AS + ( SELECT + d_week_seq, + sum(CASE WHEN (d_day_name = 'Sunday') + THEN sales_price + ELSE NULL END) + sun_sales, + sum(CASE WHEN (d_day_name = 'Monday') + THEN sales_price + ELSE NULL END) + mon_sales, + sum(CASE WHEN (d_day_name = 'Tuesday') + THEN sales_price + ELSE NULL END) + tue_sales, + sum(CASE WHEN (d_day_name = 'Wednesday') + THEN sales_price + ELSE NULL END) + wed_sales, + sum(CASE WHEN (d_day_name = 'Thursday') + THEN sales_price + ELSE NULL END) + thu_sales, + sum(CASE WHEN (d_day_name = 'Friday') + THEN sales_price + ELSE NULL END) + fri_sales, + sum(CASE WHEN (d_day_name = 'Saturday') + THEN sales_price + ELSE NULL END) + sat_sales + FROM wscs, date_dim[ TABLE_SUFFIX ] + WHERE d_date_sk = sold_date_sk + GROUP BY d_week_seq) +SELECT + d_week_seq1, + round(sun_sales1 / sun_sales2, 2), + round(mon_sales1 / mon_sales2, 2), + round(tue_sales1 / tue_sales2, 2), + round(wed_sales1 / wed_sales2, 2), + round(thu_sales1 / thu_sales2, 2), + round(fri_sales1 / fri_sales2, 2), + round(sat_sales1 / sat_sales2, 2) +FROM + (SELECT + wswscs.d_week_seq d_week_seq1, + sun_sales sun_sales1, + mon_sales mon_sales1, + tue_sales tue_sales1, + wed_sales wed_sales1, + thu_sales thu_sales1, + fri_sales fri_sales1, + sat_sales sat_sales1 + FROM wswscs, date_dim[ TABLE_SUFFIX ] + WHERE date_dim[ TABLE_SUFFIX ].d_week_seq = wswscs.d_week_seq AND d_year = 2001) y, + (SELECT + wswscs.d_week_seq d_week_seq2, + sun_sales sun_sales2, + mon_sales mon_sales2, + tue_sales tue_sales2, + wed_sales wed_sales2, + thu_sales thu_sales2, + fri_sales fri_sales2, + sat_sales sat_sales2 + FROM wswscs, date_dim[ TABLE_SUFFIX ] + WHERE date_dim[ TABLE_SUFFIX ].d_week_seq = wswscs.d_week_seq AND d_year = 2001 + 1) z +WHERE d_week_seq1 = d_week_seq2 - 53 +ORDER BY d_week_seq1 diff --git a/athena-tpcds/src/main/resources/queries/q20.sql b/athena-tpcds/src/main/resources/queries/q20.sql new file mode 100644 index 0000000000..d47f50897a --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q20.sql @@ -0,0 +1,18 @@ +SELECT + i_item_desc, + i_category, + i_class, + i_current_price, + sum(cs_ext_sales_price) AS itemrevenue, + sum(cs_ext_sales_price) * 100 / sum(sum(cs_ext_sales_price)) + OVER + (PARTITION BY i_class) AS revenueratio +FROM catalog_sales[ TABLE_SUFFIX ], item[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ] +WHERE cs_item_sk = i_item_sk + AND i_category IN ('Sports', 'Books', 'Home') + AND cs_sold_date_sk = d_date_sk + AND CAST(d_date as DATE) BETWEEN cast('1999-02-22' AS DATE) +AND (cast('1999-02-22' AS DATE) + INTERVAL '30' day) +GROUP BY i_item_id, i_item_desc, i_category, i_class, i_current_price +ORDER BY i_category, i_class, i_item_id, i_item_desc, revenueratio +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q21.sql b/athena-tpcds/src/main/resources/queries/q21.sql new file mode 100644 index 0000000000..7781a0a8db --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q21.sql @@ -0,0 +1,25 @@ +SELECT * +FROM ( + SELECT + w_warehouse_name, + i_item_id, + sum(CASE WHEN (cast(d_date AS DATE) < cast('2000-03-11' AS DATE)) + THEN inv_quantity_on_hand + ELSE 0 END) AS inv_before, + sum(CASE WHEN (cast(d_date AS DATE) >= cast('2000-03-11' AS DATE)) + THEN inv_quantity_on_hand + ELSE 0 END) AS inv_after + FROM inventory[ TABLE_SUFFIX ], warehouse[ TABLE_SUFFIX ], item[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ] + WHERE i_current_price BETWEEN 0.99 AND 1.49 + AND i_item_sk = inv_item_sk + AND inv_warehouse_sk = w_warehouse_sk + AND inv_date_sk = d_date_sk + AND CAST(d_date as DATE) BETWEEN (cast('2000-03-11' AS DATE) - INTERVAL '30' day) + AND (cast('2000-03-11' AS DATE) + INTERVAL '30' day) + GROUP BY w_warehouse_name, i_item_id) x +WHERE (CASE WHEN inv_before > 0 + THEN inv_after / inv_before + ELSE NULL + END) BETWEEN 2.0 / 3.0 AND 3.0 / 2.0 +ORDER BY w_warehouse_name, i_item_id +LIMIT 100 \ No newline at end of file diff --git a/athena-tpcds/src/main/resources/queries/q22.sql b/athena-tpcds/src/main/resources/queries/q22.sql new file mode 100644 index 0000000000..a02a38a396 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q22.sql @@ -0,0 +1,14 @@ +SELECT + i_product_name, + i_brand, + i_class, + i_category, + avg(inv_quantity_on_hand) qoh +FROM inventory[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ], item[ TABLE_SUFFIX ], warehouse[ TABLE_SUFFIX ] +WHERE inv_date_sk = d_date_sk + AND inv_item_sk = i_item_sk + AND inv_warehouse_sk = w_warehouse_sk + AND d_month_seq BETWEEN 1200 AND 1200 + 11 +GROUP BY ROLLUP (i_product_name, i_brand, i_class, i_category) +ORDER BY qoh, i_product_name, i_brand, i_class, i_category +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q23a.sql b/athena-tpcds/src/main/resources/queries/q23a.sql new file mode 100644 index 0000000000..32a3416512 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q23a.sql @@ -0,0 +1,53 @@ +WITH frequent_ss_items AS +(SELECT + substr(i_item_desc, 1, 30) itemdesc, + i_item_sk item_sk, + d_date solddate, + count(*) cnt + FROM store_sales[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ], item[ TABLE_SUFFIX ] + WHERE ss_sold_date_sk = d_date_sk + AND ss_item_sk = i_item_sk + AND d_year IN (2000, 2000 + 1, 2000 + 2, 2000 + 3) + GROUP BY substr(i_item_desc, 1, 30), i_item_sk, d_date + HAVING count(*) > 4), + max_store_sales AS + (SELECT max(csales) tpcds_cmax + FROM (SELECT + c_customer_sk, + sum(ss_quantity * ss_sales_price) csales + FROM store_sales[ TABLE_SUFFIX ], customer[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ] + WHERE ss_customer_sk = c_customer_sk + AND ss_sold_date_sk = d_date_sk + AND d_year IN (2000, 2000 + 1, 2000 + 2, 2000 + 3) + GROUP BY c_customer_sk) x), + best_ss_customer AS + (SELECT + c_customer_sk, + sum(ss_quantity * ss_sales_price) ssales + FROM store_sales[ TABLE_SUFFIX ], customer[ TABLE_SUFFIX ] + WHERE ss_customer_sk = c_customer_sk + GROUP BY c_customer_sk + HAVING sum(ss_quantity * ss_sales_price) > (50 / 100.0) * + (SELECT * + FROM max_store_sales)) +SELECT sum(sales) +FROM ((SELECT cs_quantity * cs_list_price sales +FROM catalog_sales[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ] +WHERE d_year = 2000 + AND d_moy = 2 + AND cs_sold_date_sk = d_date_sk + AND cs_item_sk IN (SELECT item_sk +FROM frequent_ss_items) + AND cs_bill_customer_sk IN (SELECT c_customer_sk +FROM best_ss_customer)) + UNION ALL + (SELECT ws_quantity * ws_list_price sales + FROM web_sales[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ] + WHERE d_year = 2000 + AND d_moy = 2 + AND ws_sold_date_sk = d_date_sk + AND ws_item_sk IN (SELECT item_sk + FROM frequent_ss_items) + AND ws_bill_customer_sk IN (SELECT c_customer_sk + FROM best_ss_customer))) y +LIMIT 100 \ No newline at end of file diff --git a/athena-tpcds/src/main/resources/queries/q23b.sql b/athena-tpcds/src/main/resources/queries/q23b.sql new file mode 100644 index 0000000000..04ff14e197 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q23b.sql @@ -0,0 +1,67 @@ +WITH frequent_ss_items AS +(SELECT + substr(i_item_desc, 1, 30) itemdesc, + i_item_sk item_sk, + d_date solddate, + count(*) cnt + FROM store_sales[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ], item[ TABLE_SUFFIX ] + WHERE ss_sold_date_sk = d_date_sk + AND ss_item_sk = i_item_sk + AND d_year IN (2000, 2000 + 1, 2000 + 2, 2000 + 3) + GROUP BY substr(i_item_desc, 1, 30), i_item_sk, d_date + HAVING count(*) > 4), + max_store_sales AS + (SELECT max(csales) tpcds_cmax + FROM (SELECT + c_customer_sk, + sum(ss_quantity * ss_sales_price) csales + FROM store_sales[ TABLE_SUFFIX ], customer[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ] + WHERE ss_customer_sk = c_customer_sk + AND ss_sold_date_sk = d_date_sk + AND d_year IN (2000, 2000 + 1, 2000 + 2, 2000 + 3) + GROUP BY c_customer_sk) x), + best_ss_customer AS + (SELECT + c_customer_sk, + sum(ss_quantity * ss_sales_price) ssales + FROM store_sales[ TABLE_SUFFIX ], customer[ TABLE_SUFFIX ] + WHERE ss_customer_sk = c_customer_sk + GROUP BY c_customer_sk + HAVING sum(ss_quantity * ss_sales_price) > (50 / 100.0) * + (SELECT * + FROM max_store_sales)) +SELECT + c_last_name, + c_first_name, + sales +FROM ((SELECT + c_last_name, + c_first_name, + sum(cs_quantity * cs_list_price) sales +FROM catalog_sales[ TABLE_SUFFIX ], customer[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ] +WHERE d_year = 2000 + AND d_moy = 2 + AND cs_sold_date_sk = d_date_sk + AND cs_item_sk IN (SELECT item_sk +FROM frequent_ss_items) + AND cs_bill_customer_sk IN (SELECT c_customer_sk +FROM best_ss_customer) + AND cs_bill_customer_sk = c_customer_sk +GROUP BY c_last_name, c_first_name) + UNION ALL + (SELECT + c_last_name, + c_first_name, + sum(ws_quantity * ws_list_price) sales + FROM web_sales, customer, date_dim + WHERE d_year = 2000 + AND d_moy = 2 + AND ws_sold_date_sk = d_date_sk + AND ws_item_sk IN (SELECT item_sk + FROM frequent_ss_items) + AND ws_bill_customer_sk IN (SELECT c_customer_sk + FROM best_ss_customer) + AND ws_bill_customer_sk = c_customer_sk + GROUP BY c_last_name, c_first_name)) y +ORDER BY c_last_name, c_first_name, sales +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q24a.sql b/athena-tpcds/src/main/resources/queries/q24a.sql new file mode 100644 index 0000000000..a6efec1d3b --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q24a.sql @@ -0,0 +1,34 @@ +WITH ssales AS +(SELECT + c_last_name, + c_first_name, + s_store_name, + ca_state, + s_state, + i_color, + i_current_price, + i_manager_id, + i_units, + i_size, + sum(ss_net_paid) netpaid + FROM store_sales[ TABLE_SUFFIX ], store_returns[ TABLE_SUFFIX ], store[ TABLE_SUFFIX ], item[ TABLE_SUFFIX ], customer[ TABLE_SUFFIX ], customer_address[ TABLE_SUFFIX ] + WHERE ss_ticket_number = sr_ticket_number + AND ss_item_sk = sr_item_sk + AND ss_customer_sk = c_customer_sk + AND ss_item_sk = i_item_sk + AND ss_store_sk = s_store_sk + AND c_birth_country = upper(ca_country) + AND s_zip = ca_zip + AND s_market_id = 8 + GROUP BY c_last_name, c_first_name, s_store_name, ca_state, s_state, i_color, + i_current_price, i_manager_id, i_units, i_size) +SELECT + c_last_name, + c_first_name, + s_store_name, + sum(netpaid) paid +FROM ssales +WHERE i_color = 'pale' +GROUP BY c_last_name, c_first_name, s_store_name +HAVING sum(netpaid) > (SELECT 0.05 * avg(netpaid) +FROM ssales) \ No newline at end of file diff --git a/athena-tpcds/src/main/resources/queries/q24b.sql b/athena-tpcds/src/main/resources/queries/q24b.sql new file mode 100644 index 0000000000..9d23f6857c --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q24b.sql @@ -0,0 +1,34 @@ +WITH ssales AS +(SELECT + c_last_name, + c_first_name, + s_store_name, + ca_state, + s_state, + i_color, + i_current_price, + i_manager_id, + i_units, + i_size, + sum(ss_net_paid) netpaid + FROM store_sales[ TABLE_SUFFIX ], store_returns[ TABLE_SUFFIX ], store[ TABLE_SUFFIX ], item[ TABLE_SUFFIX ], customer[ TABLE_SUFFIX ], customer_address[ TABLE_SUFFIX ] + WHERE ss_ticket_number = sr_ticket_number + AND ss_item_sk = sr_item_sk + AND ss_customer_sk = c_customer_sk + AND ss_item_sk = i_item_sk + AND ss_store_sk = s_store_sk + AND c_birth_country = upper(ca_country) + AND s_zip = ca_zip + AND s_market_id = 8 + GROUP BY c_last_name, c_first_name, s_store_name, ca_state, s_state, + i_color, i_current_price, i_manager_id, i_units, i_size) +SELECT + c_last_name, + c_first_name, + s_store_name, + sum(netpaid) paid +FROM ssales +WHERE i_color = 'chiffon' +GROUP BY c_last_name, c_first_name, s_store_name +HAVING sum(netpaid) > (SELECT 0.05 * avg(netpaid) +FROM ssales) diff --git a/athena-tpcds/src/main/resources/queries/q25.sql b/athena-tpcds/src/main/resources/queries/q25.sql new file mode 100644 index 0000000000..4aa8fca199 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q25.sql @@ -0,0 +1,33 @@ +SELECT + i_item_id, + i_item_desc, + s_store_id, + s_store_name, + sum(ss_net_profit) AS store_sales_profit, + sum(sr_net_loss) AS store_returns_loss, + sum(cs_net_profit) AS catalog_sales_profit +FROM + store_sales[ TABLE_SUFFIX ], store_returns[ TABLE_SUFFIX ], catalog_sales[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ] d1, + date_dim[ TABLE_SUFFIX ] d2, date_dim[ TABLE_SUFFIX ] d3, store[ TABLE_SUFFIX ], item[ TABLE_SUFFIX ] +WHERE + d1.d_moy = 4 + AND d1.d_year = 2001 + AND d1.d_date_sk = ss_sold_date_sk + AND i_item_sk = ss_item_sk + AND s_store_sk = ss_store_sk + AND ss_customer_sk = sr_customer_sk + AND ss_item_sk = sr_item_sk + AND ss_ticket_number = sr_ticket_number + AND sr_returned_date_sk = d2.d_date_sk + AND d2.d_moy BETWEEN 4 AND 10 + AND d2.d_year = 2001 + AND sr_customer_sk = cs_bill_customer_sk + AND sr_item_sk = cs_item_sk + AND cs_sold_date_sk = d3.d_date_sk + AND d3.d_moy BETWEEN 4 AND 10 + AND d3.d_year = 2001 +GROUP BY + i_item_id, i_item_desc, s_store_id, s_store_name +ORDER BY + i_item_id, i_item_desc, s_store_id, s_store_name +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q26.sql b/athena-tpcds/src/main/resources/queries/q26.sql new file mode 100644 index 0000000000..b66f74ddee --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q26.sql @@ -0,0 +1,19 @@ +SELECT + i_item_id, + avg(cs_quantity) agg1, + avg(cs_list_price) agg2, + avg(cs_coupon_amt) agg3, + avg(cs_sales_price) agg4 +FROM catalog_sales[ TABLE_SUFFIX ], customer_demographics[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ], item[ TABLE_SUFFIX ], promotion[ TABLE_SUFFIX ] +WHERE cs_sold_date_sk = d_date_sk AND + cs_item_sk = i_item_sk AND + cs_bill_cdemo_sk = cd_demo_sk AND + cs_promo_sk = p_promo_sk AND + cd_gender = 'M' AND + cd_marital_status = 'S' AND + cd_education_status = 'College' AND + (p_channel_email = 'N' OR p_channel_event = 'N') AND + d_year = 2000 +GROUP BY i_item_id +ORDER BY i_item_id +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q27.sql b/athena-tpcds/src/main/resources/queries/q27.sql new file mode 100644 index 0000000000..907f78124f --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q27.sql @@ -0,0 +1,21 @@ +SELECT + i_item_id, + s_state, + grouping(s_state) g_state, + avg(ss_quantity) agg1, + avg(ss_list_price) agg2, + avg(ss_coupon_amt) agg3, + avg(ss_sales_price) agg4 +FROM store_sales[ TABLE_SUFFIX ], customer_demographics[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ], store[ TABLE_SUFFIX ], item[ TABLE_SUFFIX ] +WHERE ss_sold_date_sk = d_date_sk AND + ss_item_sk = i_item_sk AND + ss_store_sk = s_store_sk AND + ss_cdemo_sk = cd_demo_sk AND + cd_gender = 'M' AND + cd_marital_status = 'S' AND + cd_education_status = 'College' AND + d_year = 2002 AND + s_state IN ('TN', 'TN', 'TN', 'TN', 'TN', 'TN') +GROUP BY ROLLUP (i_item_id, s_state) +ORDER BY i_item_id, s_state +LIMIT 100 \ No newline at end of file diff --git a/athena-tpcds/src/main/resources/queries/q28.sql b/athena-tpcds/src/main/resources/queries/q28.sql new file mode 100644 index 0000000000..2b9b7a5360 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q28.sql @@ -0,0 +1,56 @@ +SELECT * +FROM (SELECT + avg(ss_list_price) B1_LP, + count(ss_list_price) B1_CNT, + count(DISTINCT ss_list_price) B1_CNTD +FROM store_sales[ TABLE_SUFFIX ] +WHERE ss_quantity BETWEEN 0 AND 5 + AND (ss_list_price BETWEEN 8 AND 8 + 10 + OR ss_coupon_amt BETWEEN 459 AND 459 + 1000 + OR ss_wholesale_cost BETWEEN 57 AND 57 + 20)) B1, + (SELECT + avg(ss_list_price) B2_LP, + count(ss_list_price) B2_CNT, + count(DISTINCT ss_list_price) B2_CNTD + FROM store_sales[ TABLE_SUFFIX ] + WHERE ss_quantity BETWEEN 6 AND 10 + AND (ss_list_price BETWEEN 90 AND 90 + 10 + OR ss_coupon_amt BETWEEN 2323 AND 2323 + 1000 + OR ss_wholesale_cost BETWEEN 31 AND 31 + 20)) B2, + (SELECT + avg(ss_list_price) B3_LP, + count(ss_list_price) B3_CNT, + count(DISTINCT ss_list_price) B3_CNTD + FROM store_sales[ TABLE_SUFFIX ] + WHERE ss_quantity BETWEEN 11 AND 15 + AND (ss_list_price BETWEEN 142 AND 142 + 10 + OR ss_coupon_amt BETWEEN 12214 AND 12214 + 1000 + OR ss_wholesale_cost BETWEEN 79 AND 79 + 20)) B3, + (SELECT + avg(ss_list_price) B4_LP, + count(ss_list_price) B4_CNT, + count(DISTINCT ss_list_price) B4_CNTD + FROM store_sales[ TABLE_SUFFIX ] + WHERE ss_quantity BETWEEN 16 AND 20 + AND (ss_list_price BETWEEN 135 AND 135 + 10 + OR ss_coupon_amt BETWEEN 6071 AND 6071 + 1000 + OR ss_wholesale_cost BETWEEN 38 AND 38 + 20)) B4, + (SELECT + avg(ss_list_price) B5_LP, + count(ss_list_price) B5_CNT, + count(DISTINCT ss_list_price) B5_CNTD + FROM store_sales[ TABLE_SUFFIX ] + WHERE ss_quantity BETWEEN 21 AND 25 + AND (ss_list_price BETWEEN 122 AND 122 + 10 + OR ss_coupon_amt BETWEEN 836 AND 836 + 1000 + OR ss_wholesale_cost BETWEEN 17 AND 17 + 20)) B5, + (SELECT + avg(ss_list_price) B6_LP, + count(ss_list_price) B6_CNT, + count(DISTINCT ss_list_price) B6_CNTD + FROM store_sales[ TABLE_SUFFIX ] + WHERE ss_quantity BETWEEN 26 AND 30 + AND (ss_list_price BETWEEN 154 AND 154 + 10 + OR ss_coupon_amt BETWEEN 7326 AND 7326 + 1000 + OR ss_wholesale_cost BETWEEN 7 AND 7 + 20)) B6 +LIMIT 100 \ No newline at end of file diff --git a/athena-tpcds/src/main/resources/queries/q29.sql b/athena-tpcds/src/main/resources/queries/q29.sql new file mode 100644 index 0000000000..f6f0c0dd4a --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q29.sql @@ -0,0 +1,32 @@ +SELECT + i_item_id, + i_item_desc, + s_store_id, + s_store_name, + sum(ss_quantity) AS store_sales_quantity, + sum(sr_return_quantity) AS store_returns_quantity, + sum(cs_quantity) AS catalog_sales_quantity +FROM + store_sales[ TABLE_SUFFIX ], store_returns[ TABLE_SUFFIX ], catalog_sales[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ] d1, date_dim[ TABLE_SUFFIX ] d2, + date_dim[ TABLE_SUFFIX ] d3, store[ TABLE_SUFFIX ], item[ TABLE_SUFFIX ] +WHERE + d1.d_moy = 9 + AND d1.d_year = 1999 + AND d1.d_date_sk = ss_sold_date_sk + AND i_item_sk = ss_item_sk + AND s_store_sk = ss_store_sk + AND ss_customer_sk = sr_customer_sk + AND ss_item_sk = sr_item_sk + AND ss_ticket_number = sr_ticket_number + AND sr_returned_date_sk = d2.d_date_sk + AND d2.d_moy BETWEEN 9 AND 9 + 3 + AND d2.d_year = 1999 + AND sr_customer_sk = cs_bill_customer_sk + AND sr_item_sk = cs_item_sk + AND cs_sold_date_sk = d3.d_date_sk + AND d3.d_year IN (1999, 1999 + 1, 1999 + 2) +GROUP BY + i_item_id, i_item_desc, s_store_id, s_store_name +ORDER BY + i_item_id, i_item_desc, s_store_id, s_store_name +LIMIT 100 \ No newline at end of file diff --git a/athena-tpcds/src/main/resources/queries/q3.sql b/athena-tpcds/src/main/resources/queries/q3.sql new file mode 100644 index 0000000000..4ec201ef16 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q3.sql @@ -0,0 +1,13 @@ +SELECT + dt.d_year, + it.i_brand_id brand_id, + it.i_brand brand, + SUM(ss_ext_sales_price) sum_agg +FROM date_dim[ TABLE_SUFFIX ] dt, store_sales[ TABLE_SUFFIX ] ss, item[ TABLE_SUFFIX ] it +WHERE dt.d_date_sk = ss.ss_sold_date_sk + AND ss.ss_item_sk = it.i_item_sk + AND it.i_manufact_id = 128 + AND dt.d_moy = 11 +GROUP BY dt.d_year, it.i_brand, it.i_brand_id +ORDER BY dt.d_year, sum_agg DESC, brand_id +LIMIT 100 \ No newline at end of file diff --git a/athena-tpcds/src/main/resources/queries/q30.sql b/athena-tpcds/src/main/resources/queries/q30.sql new file mode 100644 index 0000000000..986bef566d --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q30.sql @@ -0,0 +1,35 @@ +WITH customer_total_return AS +(SELECT + wr_returning_customer_sk AS ctr_customer_sk, + ca_state AS ctr_state, + sum(wr_return_amt) AS ctr_total_return + FROM web_returns, date_dim, customer_address + WHERE wr_returned_date_sk = d_date_sk + AND d_year = 2002 + AND wr_returning_addr_sk = ca_address_sk + GROUP BY wr_returning_customer_sk, ca_state) +SELECT + c_customer_id, + c_salutation, + c_first_name, + c_last_name, + c_preferred_cust_flag, + c_birth_day, + c_birth_month, + c_birth_year, + c_birth_country, + c_login, + c_email_address, + c_last_review_date, + ctr_total_return +FROM customer_total_return ctr1, customer_address, customer +WHERE ctr1.ctr_total_return > (SELECT avg(ctr_total_return) * 1.2 +FROM customer_total_return ctr2 +WHERE ctr1.ctr_state = ctr2.ctr_state) + AND ca_address_sk = c_current_addr_sk + AND ca_state = 'GA' + AND ctr1.ctr_customer_sk = c_customer_sk +ORDER BY c_customer_id, c_salutation, c_first_name, c_last_name, c_preferred_cust_flag + , c_birth_day, c_birth_month, c_birth_year, c_birth_country, c_login, c_email_address + , c_last_review_date, ctr_total_return +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q31.sql b/athena-tpcds/src/main/resources/queries/q31.sql new file mode 100644 index 0000000000..fec45d5ecc --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q31.sql @@ -0,0 +1,60 @@ +WITH ss AS +(SELECT + ca_county, + d_qoy, + d_year, + sum(ss_ext_sales_price) AS store_sales + FROM store_sales[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ], customer_address[ TABLE_SUFFIX ] + WHERE ss_sold_date_sk = d_date_sk + AND ss_addr_sk = ca_address_sk + GROUP BY ca_county, d_qoy, d_year), + ws AS + (SELECT + ca_county, + d_qoy, + d_year, + sum(ws_ext_sales_price) AS web_sales + FROM web_sales[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ], customer_address[ TABLE_SUFFIX ] + WHERE ws_sold_date_sk = d_date_sk + AND ws_bill_addr_sk = ca_address_sk + GROUP BY ca_county, d_qoy, d_year) +SELECT + ss1.ca_county, + ss1.d_year, + ws2.web_sales / ws1.web_sales web_q1_q2_increase, + ss2.store_sales / ss1.store_sales store_q1_q2_increase, + ws3.web_sales / ws2.web_sales web_q2_q3_increase, + ss3.store_sales / ss2.store_sales store_q2_q3_increase +FROM + ss ss1, ss ss2, ss ss3, ws ws1, ws ws2, ws ws3 +WHERE + ss1.d_qoy = 1 + AND ss1.d_year = 2000 + AND ss1.ca_county = ss2.ca_county + AND ss2.d_qoy = 2 + AND ss2.d_year = 2000 + AND ss2.ca_county = ss3.ca_county + AND ss3.d_qoy = 3 + AND ss3.d_year = 2000 + AND ss1.ca_county = ws1.ca_county + AND ws1.d_qoy = 1 + AND ws1.d_year = 2000 + AND ws1.ca_county = ws2.ca_county + AND ws2.d_qoy = 2 + AND ws2.d_year = 2000 + AND ws1.ca_county = ws3.ca_county + AND ws3.d_qoy = 3 + AND ws3.d_year = 2000 + AND CASE WHEN ws1.web_sales > 0 + THEN ws2.web_sales / ws1.web_sales + ELSE NULL END + > CASE WHEN ss1.store_sales > 0 + THEN ss2.store_sales / ss1.store_sales + ELSE NULL END + AND CASE WHEN ws2.web_sales > 0 + THEN ws3.web_sales / ws2.web_sales + ELSE NULL END + > CASE WHEN ss2.store_sales > 0 + THEN ss3.store_sales / ss2.store_sales + ELSE NULL END +ORDER BY ss1.ca_county diff --git a/athena-tpcds/src/main/resources/queries/q32.sql b/athena-tpcds/src/main/resources/queries/q32.sql new file mode 100644 index 0000000000..c6eb6d25d0 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q32.sql @@ -0,0 +1,15 @@ +SELECT 1 AS "excess discount amount " +FROM + catalog_sales, item, date_dim +WHERE + i_manufact_id = 977 + AND i_item_sk = cs_item_sk + AND CAST(d_date as DATE) BETWEEN cast('2000-01-27' AS DATE) AND (cast('2000-01-27' AS DATE) + interval '90' day) + AND d_date_sk = cs_sold_date_sk + AND cs_ext_discount_amt > ( + SELECT 1.3 * avg(cs_ext_discount_amt) + FROM catalog_sales, date_dim + WHERE cs_item_sk = i_item_sk + AND CAST(d_date as DATE) BETWEEN cast('2000-01-27' AS DATE) AND (cast('2000-01-27' AS DATE) + interval '90' day) + AND d_date_sk = cs_sold_date_sk) +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q33.sql b/athena-tpcds/src/main/resources/queries/q33.sql new file mode 100644 index 0000000000..20e1cc9236 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q33.sql @@ -0,0 +1,65 @@ +WITH ss AS ( + SELECT + i_manufact_id, + sum(ss_ext_sales_price) total_sales + FROM + store_sales[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ], customer_address[ TABLE_SUFFIX ], item[ TABLE_SUFFIX ] + WHERE + i_manufact_id IN (SELECT i_manufact_id + FROM item[ TABLE_SUFFIX ] + WHERE i_category IN ('Electronics')) + AND ss_item_sk = i_item_sk + AND ss_sold_date_sk = d_date_sk + AND d_year = 1998 + AND d_moy = 5 + AND ss_addr_sk = ca_address_sk + AND ca_gmt_offset = -5 + GROUP BY i_manufact_id), cs AS +(SELECT + i_manufact_id, + sum(cs_ext_sales_price) total_sales + FROM catalog_sales[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ], customer_address[ TABLE_SUFFIX ], item[ TABLE_SUFFIX ] + WHERE + i_manufact_id IN ( + SELECT i_manufact_id + FROM item[ TABLE_SUFFIX ] + WHERE + i_category IN ('Electronics')) + AND cs_item_sk = i_item_sk + AND cs_sold_date_sk = d_date_sk + AND d_year = 1998 + AND d_moy = 5 + AND cs_bill_addr_sk = ca_address_sk + AND ca_gmt_offset = -5 + GROUP BY i_manufact_id), + ws AS ( + SELECT + i_manufact_id, + sum(ws_ext_sales_price) total_sales + FROM + web_sales[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ], customer_address[ TABLE_SUFFIX ], item[ TABLE_SUFFIX ] + WHERE + i_manufact_id IN (SELECT i_manufact_id + FROM item[ TABLE_SUFFIX ] + WHERE i_category IN ('Electronics')) + AND ws_item_sk = i_item_sk + AND ws_sold_date_sk = d_date_sk + AND d_year = 1998 + AND d_moy = 5 + AND ws_bill_addr_sk = ca_address_sk + AND ca_gmt_offset = -5 + GROUP BY i_manufact_id) +SELECT + i_manufact_id, + sum(total_sales) total_sales +FROM (SELECT * + FROM ss + UNION ALL + SELECT * + FROM cs + UNION ALL + SELECT * + FROM ws) tmp1 +GROUP BY i_manufact_id +ORDER BY total_sales +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q34.sql b/athena-tpcds/src/main/resources/queries/q34.sql new file mode 100644 index 0000000000..c32c379f2f --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q34.sql @@ -0,0 +1,32 @@ +SELECT + c_last_name, + c_first_name, + c_salutation, + c_preferred_cust_flag, + ss_ticket_number, + cnt +FROM + (SELECT + ss_ticket_number, + ss_customer_sk, + count(*) cnt + FROM store_sales[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ], store[ TABLE_SUFFIX ], household_demographics[ TABLE_SUFFIX ] + WHERE store_sales[ TABLE_SUFFIX ].ss_sold_date_sk = date_dim[ TABLE_SUFFIX ].d_date_sk + AND store_sales[ TABLE_SUFFIX ].ss_store_sk = store[ TABLE_SUFFIX ].s_store_sk + AND store_sales[ TABLE_SUFFIX ].ss_hdemo_sk = household_demographics[ TABLE_SUFFIX ].hd_demo_sk + AND (date_dim[ TABLE_SUFFIX ].d_dom BETWEEN 1 AND 3 OR date_dim[ TABLE_SUFFIX ].d_dom BETWEEN 25 AND 28) + AND (household_demographics[ TABLE_SUFFIX ].hd_buy_potential = '>10000' OR + household_demographics[ TABLE_SUFFIX ].hd_buy_potential = 'unknown') + AND household_demographics[ TABLE_SUFFIX ].hd_vehicle_count > 0 + AND (CASE WHEN household_demographics[ TABLE_SUFFIX ].hd_vehicle_count > 0 + THEN household_demographics[ TABLE_SUFFIX ].hd_dep_count / household_demographics[ TABLE_SUFFIX ].hd_vehicle_count + ELSE NULL + END) > 1.2 + AND date_dim[ TABLE_SUFFIX ].d_year IN (1999, 1999 + 1, 1999 + 2) + AND store[ TABLE_SUFFIX ].s_county IN + ('Williamson County', 'Williamson County', 'Williamson County', 'Williamson County', + 'Williamson County', 'Williamson County', 'Williamson County', 'Williamson County') + GROUP BY ss_ticket_number, ss_customer_sk) dn, customer +WHERE ss_customer_sk = c_customer_sk + AND cnt BETWEEN 15 AND 20 +ORDER BY c_last_name, c_first_name, c_salutation, c_preferred_cust_flag DESC diff --git a/athena-tpcds/src/main/resources/queries/q35.sql b/athena-tpcds/src/main/resources/queries/q35.sql new file mode 100644 index 0000000000..cfe4342d8b --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q35.sql @@ -0,0 +1,46 @@ +SELECT + ca_state, + cd_gender, + cd_marital_status, + count(*) cnt1, + min(cd_dep_count), + max(cd_dep_count), + avg(cd_dep_count), + cd_dep_employed_count, + count(*) cnt2, + min(cd_dep_employed_count), + max(cd_dep_employed_count), + avg(cd_dep_employed_count), + cd_dep_college_count, + count(*) cnt3, + min(cd_dep_college_count), + max(cd_dep_college_count), + avg(cd_dep_college_count) +FROM + customer c, customer_address ca, customer_demographics +WHERE + c.c_current_addr_sk = ca.ca_address_sk AND + cd_demo_sk = c.c_current_cdemo_sk AND + exists(SELECT * + FROM store_sales, date_dim + WHERE c.c_customer_sk = ss_customer_sk AND + ss_sold_date_sk = d_date_sk AND + d_year = 2002 AND + d_qoy < 4) AND + (exists(SELECT * + FROM web_sales, date_dim + WHERE c.c_customer_sk = ws_bill_customer_sk AND + ws_sold_date_sk = d_date_sk AND + d_year = 2002 AND + d_qoy < 4) OR + exists(SELECT * + FROM catalog_sales, date_dim + WHERE c.c_customer_sk = cs_ship_customer_sk AND + cs_sold_date_sk = d_date_sk AND + d_year = 2002 AND + d_qoy < 4)) +GROUP BY ca_state, cd_gender, cd_marital_status, cd_dep_count, + cd_dep_employed_count, cd_dep_college_count +ORDER BY ca_state, cd_gender, cd_marital_status, cd_dep_count, + cd_dep_employed_count, cd_dep_college_count +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q37.sql b/athena-tpcds/src/main/resources/queries/q37.sql new file mode 100644 index 0000000000..b87f65467e --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q37.sql @@ -0,0 +1,15 @@ +SELECT + i_item_id, + i_item_desc, + i_current_price +FROM item, inventory, date_dim, catalog_sales +WHERE i_current_price BETWEEN 68 AND 68 + 30 + AND inv_item_sk = i_item_sk + AND d_date_sk = inv_date_sk + AND CAST(d_date as DATE) BETWEEN cast('2000-02-01' AS DATE) AND (cast('2000-02-01' AS DATE) + INTERVAL '60' day) + AND i_manufact_id IN (677, 940, 694, 808) + AND inv_quantity_on_hand BETWEEN 100 AND 500 + AND cs_item_sk = i_item_sk +GROUP BY i_item_id, i_item_desc, i_current_price +ORDER BY i_item_id +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q38.sql b/athena-tpcds/src/main/resources/queries/q38.sql new file mode 100644 index 0000000000..ae1a40d538 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q38.sql @@ -0,0 +1,30 @@ +SELECT count(*) +FROM ( + SELECT DISTINCT + c_last_name, + c_first_name, + d_date + FROM store_sales[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ], customer[ TABLE_SUFFIX ] + WHERE store_sales[ TABLE_SUFFIX ].ss_sold_date_sk = date_dim[ TABLE_SUFFIX ].d_date_sk + AND store_sales[ TABLE_SUFFIX ].ss_customer_sk = customer[ TABLE_SUFFIX ].c_customer_sk + AND d_month_seq BETWEEN 1200 AND 1200 + 11 + INTERSECT + SELECT DISTINCT + c_last_name, + c_first_name, + d_date + FROM catalog_sales[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ], customer[ TABLE_SUFFIX ] + WHERE catalog_sales[ TABLE_SUFFIX ].cs_sold_date_sk = date_dim[ TABLE_SUFFIX ].d_date_sk + AND catalog_sales[ TABLE_SUFFIX ].cs_bill_customer_sk = customer[ TABLE_SUFFIX ].c_customer_sk + AND d_month_seq BETWEEN 1200 AND 1200 + 11 + INTERSECT + SELECT DISTINCT + c_last_name, + c_first_name, + d_date + FROM web_sales[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ], customer[ TABLE_SUFFIX ] + WHERE web_sales[ TABLE_SUFFIX ].ws_sold_date_sk = date_dim[ TABLE_SUFFIX ].d_date_sk + AND web_sales[ TABLE_SUFFIX ].ws_bill_customer_sk = customer[ TABLE_SUFFIX ].c_customer_sk + AND d_month_seq BETWEEN 1200 AND 1200 + 11 + ) hot_cust +LIMIT 100 \ No newline at end of file diff --git a/athena-tpcds/src/main/resources/queries/q39a.sql b/athena-tpcds/src/main/resources/queries/q39a.sql new file mode 100644 index 0000000000..f6f6c7f035 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q39a.sql @@ -0,0 +1,47 @@ +WITH inv AS +(SELECT + w_warehouse_name, + w_warehouse_sk, + i_item_sk, + d_moy, + stdev, + mean, + CASE mean + WHEN 0 + THEN NULL + ELSE stdev / mean END cov + FROM (SELECT + w_warehouse_name, + w_warehouse_sk, + i_item_sk, + d_moy, + stddev_samp(inv_quantity_on_hand) stdev, + avg(inv_quantity_on_hand) mean + FROM inventory[ TABLE_SUFFIX ], item[ TABLE_SUFFIX ], warehouse[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ] + WHERE inv_item_sk = i_item_sk + AND inv_warehouse_sk = w_warehouse_sk + AND inv_date_sk = d_date_sk + AND d_year = 2001 + GROUP BY w_warehouse_name, w_warehouse_sk, i_item_sk, d_moy) foo + WHERE CASE mean + WHEN 0 + THEN 0 + ELSE stdev / mean END > 1) +SELECT + inv1.w_warehouse_sk, + inv1.i_item_sk, + inv1.d_moy, + inv1.mean, + inv1.cov, + inv2.w_warehouse_sk, + inv2.i_item_sk, + inv2.d_moy, + inv2.mean, + inv2.cov +FROM inv inv1, inv inv2 +WHERE inv1.i_item_sk = inv2.i_item_sk + AND inv1.w_warehouse_sk = inv2.w_warehouse_sk + AND inv1.d_moy = 1 + AND inv2.d_moy = 1 + 1 +ORDER BY inv1.w_warehouse_sk, inv1.i_item_sk, inv1.d_moy, inv1.mean, inv1.cov + , inv2.d_moy, inv2.mean, inv2.cov \ No newline at end of file diff --git a/athena-tpcds/src/main/resources/queries/q39b.sql b/athena-tpcds/src/main/resources/queries/q39b.sql new file mode 100644 index 0000000000..898e4dbe7c --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q39b.sql @@ -0,0 +1,49 @@ +WITH inv AS +(SELECT + w_warehouse_name, + w_warehouse_sk, + i_item_sk, + d_moy, + stdev, + mean, + CASE mean + WHEN 0 + THEN NULL + ELSE stdev / mean END cov + FROM (SELECT + w_warehouse_name, + w_warehouse_sk, + i_item_sk, + d_moy, + stddev_samp(inv_quantity_on_hand) stdev, + avg(inv_quantity_on_hand) mean + FROM inventory, item, warehouse, date_dim + WHERE inv_item_sk = i_item_sk + AND inv_warehouse_sk = w_warehouse_sk + AND inv_date_sk = d_date_sk + AND d_year = 2001 + GROUP BY w_warehouse_name, w_warehouse_sk, i_item_sk, d_moy) foo + WHERE CASE mean + WHEN 0 + THEN 0 + ELSE stdev / mean END > 1) +SELECT + inv1.w_warehouse_sk, + inv1.i_item_sk, + inv1.d_moy, + inv1.mean, + inv1.cov, + inv2.w_warehouse_sk, + inv2.i_item_sk, + inv2.d_moy, + inv2.mean, + inv2.cov +FROM inv inv1, inv inv2 +WHERE inv1.i_item_sk = inv2.i_item_sk + AND inv1.w_warehouse_sk = inv2.w_warehouse_sk + AND inv1.d_moy = 1 + AND inv2.d_moy = 1 + 1 + AND inv1.cov > 1.5 +ORDER BY inv1.w_warehouse_sk, inv1.i_item_sk, inv1.d_moy, inv1.mean, inv1.cov + , inv2.d_moy, inv2.mean, inv2.cov + diff --git a/athena-tpcds/src/main/resources/queries/q4.sql b/athena-tpcds/src/main/resources/queries/q4.sql new file mode 100644 index 0000000000..e6779a8bfb --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q4.sql @@ -0,0 +1,120 @@ +WITH year_total AS ( + SELECT + c_customer_id customer_id, + c_first_name customer_first_name, + c_last_name customer_last_name, + c_preferred_cust_flag customer_preferred_cust_flag, + c_birth_country customer_birth_country, + c_login customer_login, + c_email_address customer_email_address, + d_year dyear, + sum(((ss_ext_list_price - ss_ext_wholesale_cost - ss_ext_discount_amt) + + ss_ext_sales_price) / 2) year_total, + 's' sale_type + FROM customer[ TABLE_SUFFIX ], store_sales[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ] + WHERE c_customer_sk = ss_customer_sk AND ss_sold_date_sk = d_date_sk + GROUP BY c_customer_id, + c_first_name, + c_last_name, + c_preferred_cust_flag, + c_birth_country, + c_login, + c_email_address, + d_year + UNION ALL + SELECT + c_customer_id customer_id, + c_first_name customer_first_name, + c_last_name customer_last_name, + c_preferred_cust_flag customer_preferred_cust_flag, + c_birth_country customer_birth_country, + c_login customer_login, + c_email_address customer_email_address, + d_year dyear, + sum((((cs_ext_list_price - cs_ext_wholesale_cost - cs_ext_discount_amt) + + cs_ext_sales_price) / 2)) year_total, + 'c' sale_type + FROM customer[ TABLE_SUFFIX ], catalog_sales[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ] + WHERE c_customer_sk = cs_bill_customer_sk AND cs_sold_date_sk = d_date_sk + GROUP BY c_customer_id, + c_first_name, + c_last_name, + c_preferred_cust_flag, + c_birth_country, + c_login, + c_email_address, + d_year + UNION ALL + SELECT + c_customer_id customer_id, + c_first_name customer_first_name, + c_last_name customer_last_name, + c_preferred_cust_flag customer_preferred_cust_flag, + c_birth_country customer_birth_country, + c_login customer_login, + c_email_address customer_email_address, + d_year dyear, + sum((((ws_ext_list_price - ws_ext_wholesale_cost - ws_ext_discount_amt) + ws_ext_sales_price) / + 2)) year_total, + 'w' sale_type + FROM customer[ TABLE_SUFFIX ], web_sales[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ] + WHERE c_customer_sk = ws_bill_customer_sk AND ws_sold_date_sk = d_date_sk + GROUP BY c_customer_id, + c_first_name, + c_last_name, + c_preferred_cust_flag, + c_birth_country, + c_login, + c_email_address, + d_year) +SELECT + t_s_secyear.customer_id, + t_s_secyear.customer_first_name, + t_s_secyear.customer_last_name, + t_s_secyear.customer_preferred_cust_flag, + t_s_secyear.customer_birth_country, + t_s_secyear.customer_login, + t_s_secyear.customer_email_address +FROM year_total t_s_firstyear, year_total t_s_secyear, year_total t_c_firstyear, + year_total t_c_secyear, year_total t_w_firstyear, year_total t_w_secyear +WHERE t_s_secyear.customer_id = t_s_firstyear.customer_id + AND t_s_firstyear.customer_id = t_c_secyear.customer_id + AND t_s_firstyear.customer_id = t_c_firstyear.customer_id + AND t_s_firstyear.customer_id = t_w_firstyear.customer_id + AND t_s_firstyear.customer_id = t_w_secyear.customer_id + AND t_s_firstyear.sale_type = 's' + AND t_c_firstyear.sale_type = 'c' + AND t_w_firstyear.sale_type = 'w' + AND t_s_secyear.sale_type = 's' + AND t_c_secyear.sale_type = 'c' + AND t_w_secyear.sale_type = 'w' + AND t_s_firstyear.dyear = 2001 + AND t_s_secyear.dyear = 2001 + 1 + AND t_c_firstyear.dyear = 2001 + AND t_c_secyear.dyear = 2001 + 1 + AND t_w_firstyear.dyear = 2001 + AND t_w_secyear.dyear = 2001 + 1 + AND t_s_firstyear.year_total > 0 + AND t_c_firstyear.year_total > 0 + AND t_w_firstyear.year_total > 0 + AND CASE WHEN t_c_firstyear.year_total > 0 + THEN t_c_secyear.year_total / t_c_firstyear.year_total + ELSE NULL END + > CASE WHEN t_s_firstyear.year_total > 0 + THEN t_s_secyear.year_total / t_s_firstyear.year_total + ELSE NULL END + AND CASE WHEN t_c_firstyear.year_total > 0 + THEN t_c_secyear.year_total / t_c_firstyear.year_total + ELSE NULL END + > CASE WHEN t_w_firstyear.year_total > 0 + THEN t_w_secyear.year_total / t_w_firstyear.year_total + ELSE NULL END +ORDER BY + t_s_secyear.customer_id, + t_s_secyear.customer_first_name, + t_s_secyear.customer_last_name, + t_s_secyear.customer_preferred_cust_flag, + t_s_secyear.customer_birth_country, + t_s_secyear.customer_login, + t_s_secyear.customer_email_address +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q40.sql b/athena-tpcds/src/main/resources/queries/q40.sql new file mode 100644 index 0000000000..f6f1f2fda8 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q40.sql @@ -0,0 +1,25 @@ +SELECT + w_state, + i_item_id, + sum(CASE WHEN (cast(d_date AS DATE) < cast('2000-03-11' AS DATE)) + THEN cs_sales_price - coalesce(cr_refunded_cash, 0) + ELSE 0 END) AS sales_before, + sum(CASE WHEN (cast(d_date AS DATE) >= cast('2000-03-11' AS DATE)) + THEN cs_sales_price - coalesce(cr_refunded_cash, 0) + ELSE 0 END) AS sales_after +FROM + catalog_sales + LEFT OUTER JOIN catalog_returns ON + (cs_order_number = cr_order_number + AND cs_item_sk = cr_item_sk) + , warehouse, item, date_dim +WHERE + i_current_price BETWEEN 0.99 AND 1.49 + AND i_item_sk = cs_item_sk + AND cs_warehouse_sk = w_warehouse_sk + AND cs_sold_date_sk = d_date_sk + AND CAST(d_date as DATE) BETWEEN (cast('2000-03-11' AS DATE) - INTERVAL '30' day) + AND (cast('2000-03-11' AS DATE) + INTERVAL '30' day) +GROUP BY w_state, i_item_id +ORDER BY w_state, i_item_id +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q41.sql b/athena-tpcds/src/main/resources/queries/q41.sql new file mode 100644 index 0000000000..25e317e0e2 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q41.sql @@ -0,0 +1,49 @@ +SELECT DISTINCT (i_product_name) +FROM item i1 +WHERE i_manufact_id BETWEEN 738 AND 738 + 40 + AND (SELECT count(*) AS item_cnt +FROM item +WHERE (i_manufact = i1.i_manufact AND + ((i_category = 'Women' AND + (i_color = 'powder' OR i_color = 'khaki') AND + (i_units = 'Ounce' OR i_units = 'Oz') AND + (i_size = 'medium' OR i_size = 'extra large') + ) OR + (i_category = 'Women' AND + (i_color = 'brown' OR i_color = 'honeydew') AND + (i_units = 'Bunch' OR i_units = 'Ton') AND + (i_size = 'N/A' OR i_size = 'small') + ) OR + (i_category = 'Men' AND + (i_color = 'floral' OR i_color = 'deep') AND + (i_units = 'N/A' OR i_units = 'Dozen') AND + (i_size = 'petite' OR i_size = 'large') + ) OR + (i_category = 'Men' AND + (i_color = 'light' OR i_color = 'cornflower') AND + (i_units = 'Box' OR i_units = 'Pound') AND + (i_size = 'medium' OR i_size = 'extra large') + ))) OR + (i_manufact = i1.i_manufact AND + ((i_category = 'Women' AND + (i_color = 'midnight' OR i_color = 'snow') AND + (i_units = 'Pallet' OR i_units = 'Gross') AND + (i_size = 'medium' OR i_size = 'extra large') + ) OR + (i_category = 'Women' AND + (i_color = 'cyan' OR i_color = 'papaya') AND + (i_units = 'Cup' OR i_units = 'Dram') AND + (i_size = 'N/A' OR i_size = 'small') + ) OR + (i_category = 'Men' AND + (i_color = 'orange' OR i_color = 'frosted') AND + (i_units = 'Each' OR i_units = 'Tbl') AND + (i_size = 'petite' OR i_size = 'large') + ) OR + (i_category = 'Men' AND + (i_color = 'forest' OR i_color = 'ghost') AND + (i_units = 'Lb' OR i_units = 'Bundle') AND + (i_size = 'medium' OR i_size = 'extra large') + )))) > 0 +ORDER BY i_product_name +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q42.sql b/athena-tpcds/src/main/resources/queries/q42.sql new file mode 100644 index 0000000000..66a819597d --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q42.sql @@ -0,0 +1,18 @@ +SELECT + dt.d_year, + item[ TABLE_SUFFIX ].i_category_id, + item[ TABLE_SUFFIX ].i_category, + sum(ss_ext_sales_price) +FROM date_dim[ TABLE_SUFFIX ] dt, store_sales[ TABLE_SUFFIX ], item[ TABLE_SUFFIX ] +WHERE dt.d_date_sk = store_sales[ TABLE_SUFFIX ].ss_sold_date_sk + AND store_sales[ TABLE_SUFFIX ].ss_item_sk = item[ TABLE_SUFFIX ].i_item_sk + AND item[ TABLE_SUFFIX ].i_manager_id = 1 + AND dt.d_moy = 11 + AND dt.d_year = 2000 +GROUP BY dt.d_year + , item[ TABLE_SUFFIX ].i_category_id + , item[ TABLE_SUFFIX ].i_category +ORDER BY sum(ss_ext_sales_price) DESC, dt.d_year + , item[ TABLE_SUFFIX ].i_category_id + , item[ TABLE_SUFFIX ].i_category +LIMIT 100 \ No newline at end of file diff --git a/athena-tpcds/src/main/resources/queries/q43.sql b/athena-tpcds/src/main/resources/queries/q43.sql new file mode 100644 index 0000000000..907c71fa54 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q43.sql @@ -0,0 +1,33 @@ +SELECT + s_store_name, + s_store_id, + sum(CASE WHEN (d_day_name = 'Sunday') + THEN ss_sales_price + ELSE NULL END) sun_sales, + sum(CASE WHEN (d_day_name = 'Monday') + THEN ss_sales_price + ELSE NULL END) mon_sales, + sum(CASE WHEN (d_day_name = 'Tuesday') + THEN ss_sales_price + ELSE NULL END) tue_sales, + sum(CASE WHEN (d_day_name = 'Wednesday') + THEN ss_sales_price + ELSE NULL END) wed_sales, + sum(CASE WHEN (d_day_name = 'Thursday') + THEN ss_sales_price + ELSE NULL END) thu_sales, + sum(CASE WHEN (d_day_name = 'Friday') + THEN ss_sales_price + ELSE NULL END) fri_sales, + sum(CASE WHEN (d_day_name = 'Saturday') + THEN ss_sales_price + ELSE NULL END) sat_sales +FROM date_dim[ TABLE_SUFFIX ], store_sales[ TABLE_SUFFIX ], store[ TABLE_SUFFIX ] +WHERE d_date_sk = ss_sold_date_sk AND + s_store_sk = ss_store_sk AND + s_gmt_offset = -5 AND + d_year = 2000 +GROUP BY s_store_name, s_store_id +ORDER BY s_store_name, s_store_id, sun_sales, mon_sales, tue_sales, wed_sales, + thu_sales, fri_sales, sat_sales +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q44.sql b/athena-tpcds/src/main/resources/queries/q44.sql new file mode 100644 index 0000000000..eaadc64af1 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q44.sql @@ -0,0 +1,46 @@ +SELECT + asceding.rnk, + i1.i_product_name best_performing, + i2.i_product_name worst_performing +FROM (SELECT * +FROM (SELECT + item_sk, + rank() + OVER ( + ORDER BY rank_col ASC) rnk +FROM (SELECT + ss_item_sk item_sk, + avg(ss_net_profit) rank_col +FROM store_sales[ TABLE_SUFFIX ] ss1 +WHERE ss_store_sk = 4 +GROUP BY ss_item_sk +HAVING avg(ss_net_profit) > 0.9 * (SELECT avg(ss_net_profit) rank_col +FROM store_sales[ TABLE_SUFFIX ] +WHERE ss_store_sk = 4 + AND ss_addr_sk IS NULL +GROUP BY ss_store_sk)) V1) V11 +WHERE rnk < 11) asceding, + (SELECT * + FROM (SELECT + item_sk, + rank() + OVER ( + ORDER BY rank_col DESC) rnk + FROM (SELECT + ss_item_sk item_sk, + avg(ss_net_profit) rank_col + FROM store_sales[ TABLE_SUFFIX ] ss1 + WHERE ss_store_sk = 4 + GROUP BY ss_item_sk + HAVING avg(ss_net_profit) > 0.9 * (SELECT avg(ss_net_profit) rank_col + FROM store_sales[ TABLE_SUFFIX ] + WHERE ss_store_sk = 4 + AND ss_addr_sk IS NULL + GROUP BY ss_store_sk)) V2) V21 + WHERE rnk < 11) descending, + item i1, item i2 +WHERE asceding.rnk = descending.rnk + AND i1.i_item_sk = asceding.item_sk + AND i2.i_item_sk = descending.item_sk +ORDER BY asceding.rnk +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q45.sql b/athena-tpcds/src/main/resources/queries/q45.sql new file mode 100644 index 0000000000..907438f196 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q45.sql @@ -0,0 +1,21 @@ +SELECT + ca_zip, + ca_city, + sum(ws_sales_price) +FROM web_sales, customer, customer_address, date_dim, item +WHERE ws_bill_customer_sk = c_customer_sk + AND c_current_addr_sk = ca_address_sk + AND ws_item_sk = i_item_sk + AND (substr(ca_zip, 1, 5) IN + ('85669', '86197', '88274', '83405', '86475', '85392', '85460', '80348', '81792') + OR + i_item_id IN (SELECT i_item_id + FROM item + WHERE i_item_sk IN (2, 3, 5, 7, 11, 13, 17, 19, 23, 29) + ) +) + AND ws_sold_date_sk = d_date_sk + AND d_qoy = 2 AND d_year = 2001 +GROUP BY ca_zip, ca_city +ORDER BY ca_zip, ca_city +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q46.sql b/athena-tpcds/src/main/resources/queries/q46.sql new file mode 100644 index 0000000000..0911677dff --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q46.sql @@ -0,0 +1,32 @@ +SELECT + c_last_name, + c_first_name, + ca_city, + bought_city, + ss_ticket_number, + amt, + profit +FROM + (SELECT + ss_ticket_number, + ss_customer_sk, + ca_city bought_city, + sum(ss_coupon_amt) amt, + sum(ss_net_profit) profit + FROM store_sales, date_dim, store, household_demographics, customer_address + WHERE store_sales.ss_sold_date_sk = date_dim.d_date_sk + AND store_sales.ss_store_sk = store.s_store_sk + AND store_sales.ss_hdemo_sk = household_demographics.hd_demo_sk + AND store_sales.ss_addr_sk = customer_address.ca_address_sk + AND (household_demographics.hd_dep_count = 4 OR + household_demographics.hd_vehicle_count = 3) + AND date_dim.d_dow IN (6, 0) + AND date_dim.d_year IN (1999, 1999 + 1, 1999 + 2) + AND store.s_city IN ('Fairview', 'Midway', 'Fairview', 'Fairview', 'Fairview') + GROUP BY ss_ticket_number, ss_customer_sk, ss_addr_sk, ca_city) dn, customer, + customer_address current_addr +WHERE ss_customer_sk = c_customer_sk + AND customer.c_current_addr_sk = current_addr.ca_address_sk + AND current_addr.ca_city <> bought_city +ORDER BY c_last_name, c_first_name, ca_city, bought_city, ss_ticket_number +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q47.sql b/athena-tpcds/src/main/resources/queries/q47.sql new file mode 100644 index 0000000000..cfc37a4cec --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q47.sql @@ -0,0 +1,63 @@ +WITH v1 AS ( + SELECT + i_category, + i_brand, + s_store_name, + s_company_name, + d_year, + d_moy, + sum(ss_sales_price) sum_sales, + avg(sum(ss_sales_price)) + OVER + (PARTITION BY i_category, i_brand, + s_store_name, s_company_name, d_year) + avg_monthly_sales, + rank() + OVER + (PARTITION BY i_category, i_brand, + s_store_name, s_company_name + ORDER BY d_year, d_moy) rn + FROM item, store_sales, date_dim, store + WHERE ss_item_sk = i_item_sk AND + ss_sold_date_sk = d_date_sk AND + ss_store_sk = s_store_sk AND + ( + d_year = 1999 OR + (d_year = 1999 - 1 AND d_moy = 12) OR + (d_year = 1999 + 1 AND d_moy = 1) + ) + GROUP BY i_category, i_brand, + s_store_name, s_company_name, + d_year, d_moy), + v2 AS ( + SELECT + v1.i_category, + v1.i_brand, + v1.s_store_name, + v1.s_company_name, + v1.d_year, + v1.d_moy, + v1.avg_monthly_sales, + v1.sum_sales, + v1_lag.sum_sales psum, + v1_lead.sum_sales nsum + FROM v1, v1 v1_lag, v1 v1_lead + WHERE v1.i_category = v1_lag.i_category AND + v1.i_category = v1_lead.i_category AND + v1.i_brand = v1_lag.i_brand AND + v1.i_brand = v1_lead.i_brand AND + v1.s_store_name = v1_lag.s_store_name AND + v1.s_store_name = v1_lead.s_store_name AND + v1.s_company_name = v1_lag.s_company_name AND + v1.s_company_name = v1_lead.s_company_name AND + v1.rn = v1_lag.rn + 1 AND + v1.rn = v1_lead.rn - 1) +SELECT * +FROM v2 +WHERE d_year = 1999 AND + avg_monthly_sales > 0 AND + CASE WHEN avg_monthly_sales > 0 + THEN abs(sum_sales - avg_monthly_sales) / avg_monthly_sales + ELSE NULL END > 0.1 +ORDER BY sum_sales - avg_monthly_sales, 3 +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q48.sql b/athena-tpcds/src/main/resources/queries/q48.sql new file mode 100644 index 0000000000..fdb9f38e29 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q48.sql @@ -0,0 +1,63 @@ +SELECT sum(ss_quantity) +FROM store_sales, store, customer_demographics, customer_address, date_dim +WHERE s_store_sk = ss_store_sk + AND ss_sold_date_sk = d_date_sk AND d_year = 2001 + AND + ( + ( + cd_demo_sk = ss_cdemo_sk + AND + cd_marital_status = 'M' + AND + cd_education_status = '4 yr Degree' + AND + ss_sales_price BETWEEN 100.00 AND 150.00 + ) + OR + ( + cd_demo_sk = ss_cdemo_sk + AND + cd_marital_status = 'D' + AND + cd_education_status = '2 yr Degree' + AND + ss_sales_price BETWEEN 50.00 AND 100.00 + ) + OR + ( + cd_demo_sk = ss_cdemo_sk + AND + cd_marital_status = 'S' + AND + cd_education_status = 'College' + AND + ss_sales_price BETWEEN 150.00 AND 200.00 + ) + ) + AND + ( + ( + ss_addr_sk = ca_address_sk + AND + ca_country = 'United States' + AND + ca_state IN ('CO', 'OH', 'TX') + AND ss_net_profit BETWEEN 0 AND 2000 + ) + OR + (ss_addr_sk = ca_address_sk + AND + ca_country = 'United States' + AND + ca_state IN ('OR', 'MN', 'KY') + AND ss_net_profit BETWEEN 150 AND 3000 + ) + OR + (ss_addr_sk = ca_address_sk + AND + ca_country = 'United States' + AND + ca_state IN ('VA', 'CA', 'MS') + AND ss_net_profit BETWEEN 50 AND 25000 + ) + ) diff --git a/athena-tpcds/src/main/resources/queries/q49.sql b/athena-tpcds/src/main/resources/queries/q49.sql new file mode 100644 index 0000000000..9568d8b92d --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q49.sql @@ -0,0 +1,126 @@ +SELECT + 'web' AS channel, + web.item, + web.return_ratio, + web.return_rank, + web.currency_rank +FROM ( + SELECT + item, + return_ratio, + currency_ratio, + rank() + OVER ( + ORDER BY return_ratio) AS return_rank, + rank() + OVER ( + ORDER BY currency_ratio) AS currency_rank + FROM + (SELECT + ws.ws_item_sk AS item, + (cast(sum(coalesce(wr.wr_return_quantity, 0)) AS DECIMAL(15, 4)) / + cast(sum(coalesce(ws.ws_quantity, 0)) AS DECIMAL(15, 4))) AS return_ratio, + (cast(sum(coalesce(wr.wr_return_amt, 0)) AS DECIMAL(15, 4)) / + cast(sum(coalesce(ws.ws_net_paid, 0)) AS DECIMAL(15, 4))) AS currency_ratio + FROM + web_sales ws LEFT OUTER JOIN web_returns wr + ON (ws.ws_order_number = wr.wr_order_number AND + ws.ws_item_sk = wr.wr_item_sk) + , date_dim + WHERE + wr.wr_return_amt > 10000 + AND ws.ws_net_profit > 1 + AND ws.ws_net_paid > 0 + AND ws.ws_quantity > 0 + AND ws_sold_date_sk = d_date_sk + AND d_year = 2001 + AND d_moy = 12 + GROUP BY ws.ws_item_sk + ) in_web + ) web +WHERE (web.return_rank <= 10 OR web.currency_rank <= 10) +UNION +SELECT + 'catalog' AS channel, + catalog.item, + catalog.return_ratio, + catalog.return_rank, + catalog.currency_rank +FROM ( + SELECT + item, + return_ratio, + currency_ratio, + rank() + OVER ( + ORDER BY return_ratio) AS return_rank, + rank() + OVER ( + ORDER BY currency_ratio) AS currency_rank + FROM + (SELECT + cs.cs_item_sk AS item, + (cast(sum(coalesce(cr.cr_return_quantity, 0)) AS DECIMAL(15, 4)) / + cast(sum(coalesce(cs.cs_quantity, 0)) AS DECIMAL(15, 4))) AS return_ratio, + (cast(sum(coalesce(cr.cr_return_amount, 0)) AS DECIMAL(15, 4)) / + cast(sum(coalesce(cs.cs_net_paid, 0)) AS DECIMAL(15, 4))) AS currency_ratio + FROM + catalog_sales cs LEFT OUTER JOIN catalog_returns cr + ON (cs.cs_order_number = cr.cr_order_number AND + cs.cs_item_sk = cr.cr_item_sk) + , date_dim + WHERE + cr.cr_return_amount > 10000 + AND cs.cs_net_profit > 1 + AND cs.cs_net_paid > 0 + AND cs.cs_quantity > 0 + AND cs_sold_date_sk = d_date_sk + AND d_year = 2001 + AND d_moy = 12 + GROUP BY cs.cs_item_sk + ) in_cat + ) catalog +WHERE (catalog.return_rank <= 10 OR catalog.currency_rank <= 10) +UNION +SELECT + 'store' AS channel, + store.item, + store.return_ratio, + store.return_rank, + store.currency_rank +FROM ( + SELECT + item, + return_ratio, + currency_ratio, + rank() + OVER ( + ORDER BY return_ratio) AS return_rank, + rank() + OVER ( + ORDER BY currency_ratio) AS currency_rank + FROM + (SELECT + sts.ss_item_sk AS item, + (cast(sum(coalesce(sr.sr_return_quantity, 0)) AS DECIMAL(15, 4)) / + cast(sum(coalesce(sts.ss_quantity, 0)) AS DECIMAL(15, 4))) AS return_ratio, + (cast(sum(coalesce(sr.sr_return_amt, 0)) AS DECIMAL(15, 4)) / + cast(sum(coalesce(sts.ss_net_paid, 0)) AS DECIMAL(15, 4))) AS currency_ratio + FROM + store_sales sts LEFT OUTER JOIN store_returns sr + ON (sts.ss_ticket_number = sr.sr_ticket_number AND sts.ss_item_sk = sr.sr_item_sk) + , date_dim + WHERE + sr.sr_return_amt > 10000 + AND sts.ss_net_profit > 1 + AND sts.ss_net_paid > 0 + AND sts.ss_quantity > 0 + AND ss_sold_date_sk = d_date_sk + AND d_year = 2001 + AND d_moy = 12 + GROUP BY sts.ss_item_sk + ) in_store + ) store +WHERE (store.return_rank <= 10 OR store.currency_rank <= 10) +ORDER BY 1, 4, 5 +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q5.sql b/athena-tpcds/src/main/resources/queries/q5.sql new file mode 100644 index 0000000000..f5f0d20308 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q5.sql @@ -0,0 +1,131 @@ +WITH ssr AS +( SELECT + s_store_id, + sum(sales_price) AS sales, + sum(profit) AS profit, + sum(return_amt) AS RETURNS, + sum(net_loss) AS profit_loss + FROM + (SELECT + ss_store_sk AS store_sk, + ss_sold_date_sk AS date_sk, + ss_ext_sales_price AS sales_price, + ss_net_profit AS profit, + cast(0 AS DECIMAL(7, 2)) AS return_amt, + cast(0 AS DECIMAL(7, 2)) AS net_loss + FROM store_sales + UNION ALL + SELECT + sr_store_sk AS store_sk, + sr_returned_date_sk AS date_sk, + cast(0 AS DECIMAL(7, 2)) AS sales_price, + cast(0 AS DECIMAL(7, 2)) AS profit, + sr_return_amt AS return_amt, + sr_net_loss AS net_loss + FROM store_returns) + salesreturns, date_dim, store + WHERE date_sk = d_date_sk + AND CAST(d_date as DATE) BETWEEN cast('2000-08-23' AS DATE) + AND ((cast('2000-08-23' AS DATE) + INTERVAL '14' day)) + AND store_sk = s_store_sk + GROUP BY s_store_id), + csr AS + ( SELECT + cp_catalog_page_id, + sum(sales_price) AS sales, + sum(profit) AS profit, + sum(return_amt) AS RETURNS, + sum(net_loss) AS profit_loss + FROM + (SELECT + cs_catalog_page_sk AS page_sk, + cs_sold_date_sk AS date_sk, + cs_ext_sales_price AS sales_price, + cs_net_profit AS profit, + cast(0 AS DECIMAL(7, 2)) AS return_amt, + cast(0 AS DECIMAL(7, 2)) AS net_loss + FROM catalog_sales + UNION ALL + SELECT + cr_catalog_page_sk AS page_sk, + cr_returned_date_sk AS date_sk, + cast(0 AS DECIMAL(7, 2)) AS sales_price, + cast(0 AS DECIMAL(7, 2)) AS profit, + cr_return_amount AS return_amt, + cr_net_loss AS net_loss + FROM catalog_returns + ) salesreturns, date_dim, catalog_page + WHERE date_sk = d_date_sk + AND CAST(d_date as DATE) BETWEEN cast('2000-08-23' AS DATE) + AND ((cast('2000-08-23' AS DATE) + INTERVAL '14' day)) + AND page_sk = cp_catalog_page_sk + GROUP BY cp_catalog_page_id) + , + wsr AS + ( SELECT + web_site_id, + sum(sales_price) AS sales, + sum(profit) AS profit, + sum(return_amt) AS RETURNS, + sum(net_loss) AS profit_loss + FROM + (SELECT + ws_web_site_sk AS wsr_web_site_sk, + ws_sold_date_sk AS date_sk, + ws_ext_sales_price AS sales_price, + ws_net_profit AS profit, + cast(0 AS DECIMAL(7, 2)) AS return_amt, + cast(0 AS DECIMAL(7, 2)) AS net_loss + FROM web_sales + UNION ALL + SELECT + ws_web_site_sk AS wsr_web_site_sk, + wr_returned_date_sk AS date_sk, + cast(0 AS DECIMAL(7, 2)) AS sales_price, + cast(0 AS DECIMAL(7, 2)) AS profit, + wr_return_amt AS return_amt, + wr_net_loss AS net_loss + FROM web_returns + LEFT OUTER JOIN web_sales ON + (wr_item_sk = ws_item_sk + AND wr_order_number = ws_order_number) + ) salesreturns, date_dim, web_site + WHERE date_sk = d_date_sk + AND CAST(d_date as DATE) BETWEEN cast('2000-08-23' AS DATE) + AND ((cast('2000-08-23' AS DATE) + INTERVAL '14' day)) + AND wsr_web_site_sk = web_site_sk + GROUP BY web_site_id) +SELECT + channel, + id, + sum(sales) AS sales, + sum(returns) AS returns, + sum(profit) AS profit +FROM + (SELECT + 'store channel' AS channel, + concat('store', s_store_id) AS id, + sales, + returns, + (profit - profit_loss) AS profit + FROM ssr + UNION ALL + SELECT + 'catalog channel' AS channel, + concat('catalog_page', cp_catalog_page_id) AS id, + sales, + returns, + (profit - profit_loss) AS profit + FROM csr + UNION ALL + SELECT + 'web channel' AS channel, + concat('web_site', web_site_id) AS id, + sales, + returns, + (profit - profit_loss) AS profit + FROM wsr + ) x +GROUP BY ROLLUP (channel, id) +ORDER BY channel, id +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q50.sql b/athena-tpcds/src/main/resources/queries/q50.sql new file mode 100644 index 0000000000..776d1d2865 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q50.sql @@ -0,0 +1,47 @@ +SELECT + s_store_name, + s_company_id, + s_street_number, + s_street_name, + s_street_type, + s_suite_number, + s_city, + s_county, + s_state, + s_zip, + sum(CASE WHEN (sr_returned_date_sk - ss_sold_date_sk <= 30) + THEN 1 + ELSE 0 END) AS "30 days ", + sum(CASE WHEN (sr_returned_date_sk - ss_sold_date_sk > 30) AND + (sr_returned_date_sk - ss_sold_date_sk <= 60) + THEN 1 + ELSE 0 END) AS "31 - 60 days ", + sum(CASE WHEN (sr_returned_date_sk - ss_sold_date_sk > 60) AND + (sr_returned_date_sk - ss_sold_date_sk <= 90) + THEN 1 + ELSE 0 END) AS "61 - 90 days ", + sum(CASE WHEN (sr_returned_date_sk - ss_sold_date_sk > 90) AND + (sr_returned_date_sk - ss_sold_date_sk <= 120) + THEN 1 + ELSE 0 END) AS "91 - 120 days ", + sum(CASE WHEN (sr_returned_date_sk - ss_sold_date_sk > 120) + THEN 1 + ELSE 0 END) AS ">120 days " +FROM + store_sales, store_returns, store, date_dim d1, date_dim d2 +WHERE + d2.d_year = 2001 + AND d2.d_moy = 8 + AND ss_ticket_number = sr_ticket_number + AND ss_item_sk = sr_item_sk + AND ss_sold_date_sk = d1.d_date_sk + AND sr_returned_date_sk = d2.d_date_sk + AND ss_customer_sk = sr_customer_sk + AND ss_store_sk = s_store_sk +GROUP BY + s_store_name, s_company_id, s_street_number, s_street_name, s_street_type, + s_suite_number, s_city, s_county, s_state, s_zip +ORDER BY + s_store_name, s_company_id, s_street_number, s_street_name, s_street_type, + s_suite_number, s_city, s_county, s_state, s_zip +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q51.sql b/athena-tpcds/src/main/resources/queries/q51.sql new file mode 100644 index 0000000000..62b003eb67 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q51.sql @@ -0,0 +1,55 @@ +WITH web_v1 AS ( + SELECT + ws_item_sk item_sk, + d_date, + sum(sum(ws_sales_price)) + OVER (PARTITION BY ws_item_sk + ORDER BY d_date + ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) cume_sales + FROM web_sales, date_dim + WHERE ws_sold_date_sk = d_date_sk + AND d_month_seq BETWEEN 1200 AND 1200 + 11 + AND ws_item_sk IS NOT NULL + GROUP BY ws_item_sk, d_date), + store_v1 AS ( + SELECT + ss_item_sk item_sk, + d_date, + sum(sum(ss_sales_price)) + OVER (PARTITION BY ss_item_sk + ORDER BY d_date + ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) cume_sales + FROM store_sales, date_dim + WHERE ss_sold_date_sk = d_date_sk + AND d_month_seq BETWEEN 1200 AND 1200 + 11 + AND ss_item_sk IS NOT NULL + GROUP BY ss_item_sk, d_date) +SELECT * +FROM (SELECT + item_sk, + d_date, + web_sales, + store_sales, + max(web_sales) + OVER (PARTITION BY item_sk + ORDER BY d_date + ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) web_cumulative, + max(store_sales) + OVER (PARTITION BY item_sk + ORDER BY d_date + ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) store_cumulative +FROM (SELECT + CASE WHEN web.item_sk IS NOT NULL + THEN web.item_sk + ELSE store.item_sk END item_sk, + CASE WHEN web.d_date IS NOT NULL + THEN web.d_date + ELSE store.d_date END d_date, + web.cume_sales web_sales, + store.cume_sales store_sales +FROM web_v1 web FULL OUTER JOIN store_v1 store ON (web.item_sk = store.item_sk + AND web.d_date = store.d_date) + ) x) y +WHERE web_cumulative > store_cumulative +ORDER BY item_sk, d_date +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q52.sql b/athena-tpcds/src/main/resources/queries/q52.sql new file mode 100644 index 0000000000..467d1ae050 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q52.sql @@ -0,0 +1,14 @@ +SELECT + dt.d_year, + item.i_brand_id brand_id, + item.i_brand brand, + sum(ss_ext_sales_price) ext_price +FROM date_dim dt, store_sales, item +WHERE dt.d_date_sk = store_sales.ss_sold_date_sk + AND store_sales.ss_item_sk = item.i_item_sk + AND item.i_manager_id = 1 + AND dt.d_moy = 11 + AND dt.d_year = 2000 +GROUP BY dt.d_year, item.i_brand, item.i_brand_id +ORDER BY dt.d_year, ext_price DESC, brand_id +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q53.sql b/athena-tpcds/src/main/resources/queries/q53.sql new file mode 100644 index 0000000000..b42c68dcf8 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q53.sql @@ -0,0 +1,30 @@ +SELECT * +FROM + (SELECT + i_manufact_id, + sum(ss_sales_price) sum_sales, + avg(sum(ss_sales_price)) + OVER (PARTITION BY i_manufact_id) avg_quarterly_sales + FROM item, store_sales, date_dim, store + WHERE ss_item_sk = i_item_sk AND + ss_sold_date_sk = d_date_sk AND + ss_store_sk = s_store_sk AND + d_month_seq IN (1200, 1200 + 1, 1200 + 2, 1200 + 3, 1200 + 4, 1200 + 5, 1200 + 6, + 1200 + 7, 1200 + 8, 1200 + 9, 1200 + 10, 1200 + 11) AND + ((i_category IN ('Books', 'Children', 'Electronics') AND + i_class IN ('personal', 'portable', 'reference', 'self-help') AND + i_brand IN ('scholaramalgamalg #14', 'scholaramalgamalg #7', + 'exportiunivamalg #9', 'scholaramalgamalg #9')) + OR + (i_category IN ('Women', 'Music', 'Men') AND + i_class IN ('accessories', 'classical', 'fragrances', 'pants') AND + i_brand IN ('amalgimporto #1', 'edu packscholar #1', 'exportiimporto #1', + 'importoamalg #1'))) + GROUP BY i_manufact_id, d_qoy) tmp1 +WHERE CASE WHEN avg_quarterly_sales > 0 + THEN abs(sum_sales - avg_quarterly_sales) / avg_quarterly_sales + ELSE NULL END > 0.1 +ORDER BY avg_quarterly_sales, + sum_sales, + i_manufact_id +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q54.sql b/athena-tpcds/src/main/resources/queries/q54.sql new file mode 100644 index 0000000000..2508677bb7 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q54.sql @@ -0,0 +1,61 @@ +WITH my_customers AS ( + SELECT DISTINCT + c_customer_sk, + c_current_addr_sk + FROM + (SELECT + cs_sold_date_sk sold_date_sk, + cs_bill_customer_sk customer_sk, + cs_item_sk item_sk + FROM catalog_sales + UNION ALL + SELECT + ws_sold_date_sk sold_date_sk, + ws_bill_customer_sk customer_sk, + ws_item_sk item_sk + FROM web_sales + ) cs_or_ws_sales, + item, + date_dim, + customer + WHERE sold_date_sk = d_date_sk + AND item_sk = i_item_sk + AND i_category = 'Women' + AND i_class = 'maternity' + AND c_customer_sk = cs_or_ws_sales.customer_sk + AND d_moy = 12 + AND d_year = 1998 +) + , my_revenue AS ( + SELECT + c_customer_sk, + sum(ss_ext_sales_price) AS revenue + FROM my_customers, + store_sales, + customer_address, + store, + date_dim + WHERE c_current_addr_sk = ca_address_sk + AND ca_county = s_county + AND ca_state = s_state + AND ss_sold_date_sk = d_date_sk + AND c_customer_sk = ss_customer_sk + AND d_month_seq BETWEEN (SELECT DISTINCT d_month_seq + 1 + FROM date_dim + WHERE d_year = 1998 AND d_moy = 12) + AND (SELECT DISTINCT d_month_seq + 3 + FROM date_dim + WHERE d_year = 1998 AND d_moy = 12) + GROUP BY c_customer_sk +) + , segments AS +(SELECT cast((revenue / 50) AS INTEGER) AS segment + FROM my_revenue) +SELECT + segment, + count(*) AS num_customers, + segment * 50 AS segment_base +FROM segments +GROUP BY segment +ORDER BY segment, num_customers +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q55.sql b/athena-tpcds/src/main/resources/queries/q55.sql new file mode 100644 index 0000000000..bc5d888c9a --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q55.sql @@ -0,0 +1,13 @@ +SELECT + i_brand_id brand_id, + i_brand brand, + sum(ss_ext_sales_price) ext_price +FROM date_dim, store_sales, item +WHERE d_date_sk = ss_sold_date_sk + AND ss_item_sk = i_item_sk + AND i_manager_id = 28 + AND d_moy = 11 + AND d_year = 1999 +GROUP BY i_brand, i_brand_id +ORDER BY ext_price DESC, brand_id +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q56.sql b/athena-tpcds/src/main/resources/queries/q56.sql new file mode 100644 index 0000000000..2fa1738dcf --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q56.sql @@ -0,0 +1,65 @@ +WITH ss AS ( + SELECT + i_item_id, + sum(ss_ext_sales_price) total_sales + FROM + store_sales, date_dim, customer_address, item + WHERE + i_item_id IN (SELECT i_item_id + FROM item + WHERE i_color IN ('slate', 'blanched', 'burnished')) + AND ss_item_sk = i_item_sk + AND ss_sold_date_sk = d_date_sk + AND d_year = 2001 + AND d_moy = 2 + AND ss_addr_sk = ca_address_sk + AND ca_gmt_offset = -5 + GROUP BY i_item_id), + cs AS ( + SELECT + i_item_id, + sum(cs_ext_sales_price) total_sales + FROM + catalog_sales, date_dim, customer_address, item + WHERE + i_item_id IN (SELECT i_item_id + FROM item + WHERE i_color IN ('slate', 'blanched', 'burnished')) + AND cs_item_sk = i_item_sk + AND cs_sold_date_sk = d_date_sk + AND d_year = 2001 + AND d_moy = 2 + AND cs_bill_addr_sk = ca_address_sk + AND ca_gmt_offset = -5 + GROUP BY i_item_id), + ws AS ( + SELECT + i_item_id, + sum(ws_ext_sales_price) total_sales + FROM + web_sales, date_dim, customer_address, item + WHERE + i_item_id IN (SELECT i_item_id + FROM item + WHERE i_color IN ('slate', 'blanched', 'burnished')) + AND ws_item_sk = i_item_sk + AND ws_sold_date_sk = d_date_sk + AND d_year = 2001 + AND d_moy = 2 + AND ws_bill_addr_sk = ca_address_sk + AND ca_gmt_offset = -5 + GROUP BY i_item_id) +SELECT + i_item_id, + sum(total_sales) total_sales +FROM (SELECT * + FROM ss + UNION ALL + SELECT * + FROM cs + UNION ALL + SELECT * + FROM ws) tmp1 +GROUP BY i_item_id +ORDER BY total_sales +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q57.sql b/athena-tpcds/src/main/resources/queries/q57.sql new file mode 100644 index 0000000000..cf70d4b905 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q57.sql @@ -0,0 +1,56 @@ +WITH v1 AS ( + SELECT + i_category, + i_brand, + cc_name, + d_year, + d_moy, + sum(cs_sales_price) sum_sales, + avg(sum(cs_sales_price)) + OVER + (PARTITION BY i_category, i_brand, cc_name, d_year) + avg_monthly_sales, + rank() + OVER + (PARTITION BY i_category, i_brand, cc_name + ORDER BY d_year, d_moy) rn + FROM item, catalog_sales, date_dim, call_center + WHERE cs_item_sk = i_item_sk AND + cs_sold_date_sk = d_date_sk AND + cc_call_center_sk = cs_call_center_sk AND + ( + d_year = 1999 OR + (d_year = 1999 - 1 AND d_moy = 12) OR + (d_year = 1999 + 1 AND d_moy = 1) + ) + GROUP BY i_category, i_brand, + cc_name, d_year, d_moy), + v2 AS ( + SELECT + v1.i_category, + v1.i_brand, + v1.cc_name, + v1.d_year, + v1.d_moy, + v1.avg_monthly_sales, + v1.sum_sales, + v1_lag.sum_sales psum, + v1_lead.sum_sales nsum + FROM v1, v1 v1_lag, v1 v1_lead + WHERE v1.i_category = v1_lag.i_category AND + v1.i_category = v1_lead.i_category AND + v1.i_brand = v1_lag.i_brand AND + v1.i_brand = v1_lead.i_brand AND + v1.cc_name = v1_lag.cc_name AND + v1.cc_name = v1_lead.cc_name AND + v1.rn = v1_lag.rn + 1 AND + v1.rn = v1_lead.rn - 1) +SELECT * +FROM v2 +WHERE d_year = 1999 AND + avg_monthly_sales > 0 AND + CASE WHEN avg_monthly_sales > 0 + THEN abs(sum_sales - avg_monthly_sales) / avg_monthly_sales + ELSE NULL END > 0.1 +ORDER BY sum_sales - avg_monthly_sales, 3 +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q59.sql b/athena-tpcds/src/main/resources/queries/q59.sql new file mode 100644 index 0000000000..3cef202768 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q59.sql @@ -0,0 +1,75 @@ +WITH wss AS +(SELECT + d_week_seq, + ss_store_sk, + sum(CASE WHEN (d_day_name = 'Sunday') + THEN ss_sales_price + ELSE NULL END) sun_sales, + sum(CASE WHEN (d_day_name = 'Monday') + THEN ss_sales_price + ELSE NULL END) mon_sales, + sum(CASE WHEN (d_day_name = 'Tuesday') + THEN ss_sales_price + ELSE NULL END) tue_sales, + sum(CASE WHEN (d_day_name = 'Wednesday') + THEN ss_sales_price + ELSE NULL END) wed_sales, + sum(CASE WHEN (d_day_name = 'Thursday') + THEN ss_sales_price + ELSE NULL END) thu_sales, + sum(CASE WHEN (d_day_name = 'Friday') + THEN ss_sales_price + ELSE NULL END) fri_sales, + sum(CASE WHEN (d_day_name = 'Saturday') + THEN ss_sales_price + ELSE NULL END) sat_sales + FROM store_sales, date_dim + WHERE d_date_sk = ss_sold_date_sk + GROUP BY d_week_seq, ss_store_sk +) +SELECT + s_store_name1, + s_store_id1, + d_week_seq1, + sun_sales1 / sun_sales2, + mon_sales1 / mon_sales2, + tue_sales1 / tue_sales2, + wed_sales1 / wed_sales2, + thu_sales1 / thu_sales2, + fri_sales1 / fri_sales2, + sat_sales1 / sat_sales2 +FROM + (SELECT + s_store_name s_store_name1, + wss.d_week_seq d_week_seq1, + s_store_id s_store_id1, + sun_sales sun_sales1, + mon_sales mon_sales1, + tue_sales tue_sales1, + wed_sales wed_sales1, + thu_sales thu_sales1, + fri_sales fri_sales1, + sat_sales sat_sales1 + FROM wss, store, date_dim d + WHERE d.d_week_seq = wss.d_week_seq AND + ss_store_sk = s_store_sk AND + d_month_seq BETWEEN 1212 AND 1212 + 11) y, + (SELECT + s_store_name s_store_name2, + wss.d_week_seq d_week_seq2, + s_store_id s_store_id2, + sun_sales sun_sales2, + mon_sales mon_sales2, + tue_sales tue_sales2, + wed_sales wed_sales2, + thu_sales thu_sales2, + fri_sales fri_sales2, + sat_sales sat_sales2 + FROM wss, store, date_dim d + WHERE d.d_week_seq = wss.d_week_seq AND + ss_store_sk = s_store_sk AND + d_month_seq BETWEEN 1212 + 12 AND 1212 + 23) x +WHERE s_store_id1 = s_store_id2 + AND d_week_seq1 = d_week_seq2 - 52 +ORDER BY s_store_name1, s_store_id1, d_week_seq1 +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q6.sql b/athena-tpcds/src/main/resources/queries/q6.sql new file mode 100644 index 0000000000..a2ffec762e --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q6.sql @@ -0,0 +1,21 @@ +SELECT + a.ca_state state, + count(*) cnt +FROM + customer_address[ TABLE_SUFFIX ] a, customer[ TABLE_SUFFIX ] c, store_sales[ TABLE_SUFFIX ] s, date_dim[ TABLE_SUFFIX ] d, item[ TABLE_SUFFIX ] i +WHERE a.ca_address_sk = c.c_current_addr_sk + AND c.c_customer_sk = s.ss_customer_sk + AND s.ss_sold_date_sk = d.d_date_sk + AND s.ss_item_sk = i.i_item_sk + AND d.d_month_seq = + (SELECT DISTINCT (d_month_seq) + FROM date_dim[ TABLE_SUFFIX ] + WHERE d_year = 2000 AND d_moy = 1) + AND i.i_current_price > 1.2 * + (SELECT avg(j.i_current_price) + FROM item[ TABLE_SUFFIX ] j + WHERE j.i_category = i.i_category) +GROUP BY a.ca_state +HAVING count(*) >= 10 +ORDER BY cnt +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q60.sql b/athena-tpcds/src/main/resources/queries/q60.sql new file mode 100644 index 0000000000..41b963f44b --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q60.sql @@ -0,0 +1,62 @@ +WITH ss AS ( + SELECT + i_item_id, + sum(ss_ext_sales_price) total_sales + FROM store_sales, date_dim, customer_address, item + WHERE + i_item_id IN (SELECT i_item_id + FROM item + WHERE i_category IN ('Music')) + AND ss_item_sk = i_item_sk + AND ss_sold_date_sk = d_date_sk + AND d_year = 1998 + AND d_moy = 9 + AND ss_addr_sk = ca_address_sk + AND ca_gmt_offset = -5 + GROUP BY i_item_id), + cs AS ( + SELECT + i_item_id, + sum(cs_ext_sales_price) total_sales + FROM catalog_sales, date_dim, customer_address, item + WHERE + i_item_id IN (SELECT i_item_id + FROM item + WHERE i_category IN ('Music')) + AND cs_item_sk = i_item_sk + AND cs_sold_date_sk = d_date_sk + AND d_year = 1998 + AND d_moy = 9 + AND cs_bill_addr_sk = ca_address_sk + AND ca_gmt_offset = -5 + GROUP BY i_item_id), + ws AS ( + SELECT + i_item_id, + sum(ws_ext_sales_price) total_sales + FROM web_sales, date_dim, customer_address, item + WHERE + i_item_id IN (SELECT i_item_id + FROM item + WHERE i_category IN ('Music')) + AND ws_item_sk = i_item_sk + AND ws_sold_date_sk = d_date_sk + AND d_year = 1998 + AND d_moy = 9 + AND ws_bill_addr_sk = ca_address_sk + AND ca_gmt_offset = -5 + GROUP BY i_item_id) +SELECT + i_item_id, + sum(total_sales) total_sales +FROM (SELECT * + FROM ss + UNION ALL + SELECT * + FROM cs + UNION ALL + SELECT * + FROM ws) tmp1 +GROUP BY i_item_id +ORDER BY i_item_id, total_sales +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q61.sql b/athena-tpcds/src/main/resources/queries/q61.sql new file mode 100644 index 0000000000..b0a872b4b8 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q61.sql @@ -0,0 +1,33 @@ +SELECT + promotions, + total, + cast(promotions AS DECIMAL(15, 4)) / cast(total AS DECIMAL(15, 4)) * 100 +FROM + (SELECT sum(ss_ext_sales_price) promotions + FROM store_sales, store, promotion, date_dim, customer, customer_address, item + WHERE ss_sold_date_sk = d_date_sk + AND ss_store_sk = s_store_sk + AND ss_promo_sk = p_promo_sk + AND ss_customer_sk = c_customer_sk + AND ca_address_sk = c_current_addr_sk + AND ss_item_sk = i_item_sk + AND ca_gmt_offset = -5 + AND i_category = 'Jewelry' + AND (p_channel_dmail = 'Y' OR p_channel_email = 'Y' OR p_channel_tv = 'Y') + AND s_gmt_offset = -5 + AND d_year = 1998 + AND d_moy = 11) promotional_sales, + (SELECT sum(ss_ext_sales_price) total + FROM store_sales, store, date_dim, customer, customer_address, item + WHERE ss_sold_date_sk = d_date_sk + AND ss_store_sk = s_store_sk + AND ss_customer_sk = c_customer_sk + AND ca_address_sk = c_current_addr_sk + AND ss_item_sk = i_item_sk + AND ca_gmt_offset = -5 + AND i_category = 'Jewelry' + AND s_gmt_offset = -5 + AND d_year = 1998 + AND d_moy = 11) all_sales +ORDER BY promotions, total +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q62.sql b/athena-tpcds/src/main/resources/queries/q62.sql new file mode 100644 index 0000000000..c8779b59cd --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q62.sql @@ -0,0 +1,35 @@ +SELECT + substr(w_warehouse_name, 1, 20), + sm_type, + web_name, + sum(CASE WHEN (ws_ship_date_sk - ws_sold_date_sk <= 30) + THEN 1 + ELSE 0 END) AS "30 days ", + sum(CASE WHEN (ws_ship_date_sk - ws_sold_date_sk > 30) AND + (ws_ship_date_sk - ws_sold_date_sk <= 60) + THEN 1 + ELSE 0 END) AS "31 - 60 days ", + sum(CASE WHEN (ws_ship_date_sk - ws_sold_date_sk > 60) AND + (ws_ship_date_sk - ws_sold_date_sk <= 90) + THEN 1 + ELSE 0 END) AS "61 - 90 days ", + sum(CASE WHEN (ws_ship_date_sk - ws_sold_date_sk > 90) AND + (ws_ship_date_sk - ws_sold_date_sk <= 120) + THEN 1 + ELSE 0 END) AS "91 - 120 days ", + sum(CASE WHEN (ws_ship_date_sk - ws_sold_date_sk > 120) + THEN 1 + ELSE 0 END) AS ">120 days " +FROM + web_sales, warehouse, ship_mode, web_site, date_dim +WHERE + d_month_seq BETWEEN 1200 AND 1200 + 11 + AND ws_ship_date_sk = d_date_sk + AND ws_warehouse_sk = w_warehouse_sk + AND ws_ship_mode_sk = sm_ship_mode_sk + AND ws_web_site_sk = web_site_sk +GROUP BY + substr(w_warehouse_name, 1, 20), sm_type, web_name +ORDER BY + substr(w_warehouse_name, 1, 20), sm_type, web_name +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q63.sql b/athena-tpcds/src/main/resources/queries/q63.sql new file mode 100644 index 0000000000..ef6867e0a9 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q63.sql @@ -0,0 +1,31 @@ +SELECT * +FROM (SELECT + i_manager_id, + sum(ss_sales_price) sum_sales, + avg(sum(ss_sales_price)) + OVER (PARTITION BY i_manager_id) avg_monthly_sales +FROM item + , store_sales + , date_dim + , store +WHERE ss_item_sk = i_item_sk + AND ss_sold_date_sk = d_date_sk + AND ss_store_sk = s_store_sk + AND d_month_seq IN (1200, 1200 + 1, 1200 + 2, 1200 + 3, 1200 + 4, 1200 + 5, 1200 + 6, 1200 + 7, + 1200 + 8, 1200 + 9, 1200 + 10, 1200 + 11) + AND ((i_category IN ('Books', 'Children', 'Electronics') + AND i_class IN ('personal', 'portable', 'refernece', 'self-help') + AND i_brand IN ('scholaramalgamalg #14', 'scholaramalgamalg #7', + 'exportiunivamalg #9', 'scholaramalgamalg #9')) + OR (i_category IN ('Women', 'Music', 'Men') + AND i_class IN ('accessories', 'classical', 'fragrances', 'pants') + AND i_brand IN ('amalgimporto #1', 'edu packscholar #1', 'exportiimporto #1', + 'importoamalg #1'))) +GROUP BY i_manager_id, d_moy) tmp1 +WHERE CASE WHEN avg_monthly_sales > 0 + THEN abs(sum_sales - avg_monthly_sales) / avg_monthly_sales + ELSE NULL END > 0.1 +ORDER BY i_manager_id + , avg_monthly_sales + , sum_sales +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q64.sql b/athena-tpcds/src/main/resources/queries/q64.sql new file mode 100644 index 0000000000..8ec1d31b61 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q64.sql @@ -0,0 +1,92 @@ +WITH cs_ui AS +(SELECT + cs_item_sk, + sum(cs_ext_list_price) AS sale, + sum(cr_refunded_cash + cr_reversed_charge + cr_store_credit) AS refund + FROM catalog_sales + , catalog_returns + WHERE cs_item_sk = cr_item_sk + AND cs_order_number = cr_order_number + GROUP BY cs_item_sk + HAVING sum(cs_ext_list_price) > 2 * sum(cr_refunded_cash + cr_reversed_charge + cr_store_credit)), + cross_sales AS + (SELECT + i_product_name product_name, + i_item_sk item_sk, + s_store_name store_name, + s_zip store_zip, + ad1.ca_street_number b_street_number, + ad1.ca_street_name b_streen_name, + ad1.ca_city b_city, + ad1.ca_zip b_zip, + ad2.ca_street_number c_street_number, + ad2.ca_street_name c_street_name, + ad2.ca_city c_city, + ad2.ca_zip c_zip, + d1.d_year AS syear, + d2.d_year AS fsyear, + d3.d_year s2year, + count(*) cnt, + sum(ss_wholesale_cost) s1, + sum(ss_list_price) s2, + sum(ss_coupon_amt) s3 + FROM store_sales, store_returns, cs_ui, date_dim d1, date_dim d2, date_dim d3, + store, customer, customer_demographics cd1, customer_demographics cd2, + promotion, household_demographics hd1, household_demographics hd2, + customer_address ad1, customer_address ad2, income_band ib1, income_band ib2, item + WHERE ss_store_sk = s_store_sk AND + ss_sold_date_sk = d1.d_date_sk AND + ss_customer_sk = c_customer_sk AND + ss_cdemo_sk = cd1.cd_demo_sk AND + ss_hdemo_sk = hd1.hd_demo_sk AND + ss_addr_sk = ad1.ca_address_sk AND + ss_item_sk = i_item_sk AND + ss_item_sk = sr_item_sk AND + ss_ticket_number = sr_ticket_number AND + ss_item_sk = cs_ui.cs_item_sk AND + c_current_cdemo_sk = cd2.cd_demo_sk AND + c_current_hdemo_sk = hd2.hd_demo_sk AND + c_current_addr_sk = ad2.ca_address_sk AND + c_first_sales_date_sk = d2.d_date_sk AND + c_first_shipto_date_sk = d3.d_date_sk AND + ss_promo_sk = p_promo_sk AND + hd1.hd_income_band_sk = ib1.ib_income_band_sk AND + hd2.hd_income_band_sk = ib2.ib_income_band_sk AND + cd1.cd_marital_status <> cd2.cd_marital_status AND + i_color IN ('purple', 'burlywood', 'indian', 'spring', 'floral', 'medium') AND + i_current_price BETWEEN 64 AND 64 + 10 AND + i_current_price BETWEEN 64 + 1 AND 64 + 15 + GROUP BY i_product_name, i_item_sk, s_store_name, s_zip, ad1.ca_street_number, + ad1.ca_street_name, ad1.ca_city, ad1.ca_zip, ad2.ca_street_number, + ad2.ca_street_name, ad2.ca_city, ad2.ca_zip, d1.d_year, d2.d_year, d3.d_year + ) +SELECT + cs1.product_name, + cs1.store_name, + cs1.store_zip, + cs1.b_street_number, + cs1.b_streen_name, + cs1.b_city, + cs1.b_zip, + cs1.c_street_number, + cs1.c_street_name, + cs1.c_city, + cs1.c_zip, + cs1.syear, + cs1.cnt, + cs1.s1, + cs1.s2, + cs1.s3, + cs2.s1, + cs2.s2, + cs2.s3, + cs2.syear, + cs2.cnt +FROM cross_sales cs1, cross_sales cs2 +WHERE cs1.item_sk = cs2.item_sk AND + cs1.syear = 1999 AND + cs2.syear = 1999 + 1 AND + cs2.cnt <= cs1.cnt AND + cs1.store_name = cs2.store_name AND + cs1.store_zip = cs2.store_zip +ORDER BY cs1.product_name, cs1.store_name, cs2.cnt diff --git a/athena-tpcds/src/main/resources/queries/q65.sql b/athena-tpcds/src/main/resources/queries/q65.sql new file mode 100644 index 0000000000..aad04be1bc --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q65.sql @@ -0,0 +1,33 @@ +SELECT + s_store_name, + i_item_desc, + sc.revenue, + i_current_price, + i_wholesale_cost, + i_brand +FROM store, item, + (SELECT + ss_store_sk, + avg(revenue) AS ave + FROM + (SELECT + ss_store_sk, + ss_item_sk, + sum(ss_sales_price) AS revenue + FROM store_sales, date_dim + WHERE ss_sold_date_sk = d_date_sk AND d_month_seq BETWEEN 1176 AND 1176 + 11 + GROUP BY ss_store_sk, ss_item_sk) sa + GROUP BY ss_store_sk) sb, + (SELECT + ss_store_sk, + ss_item_sk, + sum(ss_sales_price) AS revenue + FROM store_sales, date_dim + WHERE ss_sold_date_sk = d_date_sk AND d_month_seq BETWEEN 1176 AND 1176 + 11 + GROUP BY ss_store_sk, ss_item_sk) sc +WHERE sb.ss_store_sk = sc.ss_store_sk AND + sc.revenue <= 0.1 * sb.ave AND + s_store_sk = sc.ss_store_sk AND + i_item_sk = sc.ss_item_sk +ORDER BY s_store_name, i_item_desc +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q66.sql b/athena-tpcds/src/main/resources/queries/q66.sql new file mode 100644 index 0000000000..f826b41643 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q66.sql @@ -0,0 +1,240 @@ +SELECT + w_warehouse_name, + w_warehouse_sq_ft, + w_city, + w_county, + w_state, + w_country, + ship_carriers, + year, + sum(jan_sales) AS jan_sales, + sum(feb_sales) AS feb_sales, + sum(mar_sales) AS mar_sales, + sum(apr_sales) AS apr_sales, + sum(may_sales) AS may_sales, + sum(jun_sales) AS jun_sales, + sum(jul_sales) AS jul_sales, + sum(aug_sales) AS aug_sales, + sum(sep_sales) AS sep_sales, + sum(oct_sales) AS oct_sales, + sum(nov_sales) AS nov_sales, + sum(dec_sales) AS dec_sales, + sum(jan_sales / w_warehouse_sq_ft) AS jan_sales_per_sq_foot, + sum(feb_sales / w_warehouse_sq_ft) AS feb_sales_per_sq_foot, + sum(mar_sales / w_warehouse_sq_ft) AS mar_sales_per_sq_foot, + sum(apr_sales / w_warehouse_sq_ft) AS apr_sales_per_sq_foot, + sum(may_sales / w_warehouse_sq_ft) AS may_sales_per_sq_foot, + sum(jun_sales / w_warehouse_sq_ft) AS jun_sales_per_sq_foot, + sum(jul_sales / w_warehouse_sq_ft) AS jul_sales_per_sq_foot, + sum(aug_sales / w_warehouse_sq_ft) AS aug_sales_per_sq_foot, + sum(sep_sales / w_warehouse_sq_ft) AS sep_sales_per_sq_foot, + sum(oct_sales / w_warehouse_sq_ft) AS oct_sales_per_sq_foot, + sum(nov_sales / w_warehouse_sq_ft) AS nov_sales_per_sq_foot, + sum(dec_sales / w_warehouse_sq_ft) AS dec_sales_per_sq_foot, + sum(jan_net) AS jan_net, + sum(feb_net) AS feb_net, + sum(mar_net) AS mar_net, + sum(apr_net) AS apr_net, + sum(may_net) AS may_net, + sum(jun_net) AS jun_net, + sum(jul_net) AS jul_net, + sum(aug_net) AS aug_net, + sum(sep_net) AS sep_net, + sum(oct_net) AS oct_net, + sum(nov_net) AS nov_net, + sum(dec_net) AS dec_net +FROM ( + (SELECT + w_warehouse_name, + w_warehouse_sq_ft, + w_city, + w_county, + w_state, + w_country, + concat('DHL', ',', 'BARIAN') AS ship_carriers, + d_year AS year, + sum(CASE WHEN d_moy = 1 + THEN ws_ext_sales_price * ws_quantity + ELSE 0 END) AS jan_sales, + sum(CASE WHEN d_moy = 2 + THEN ws_ext_sales_price * ws_quantity + ELSE 0 END) AS feb_sales, + sum(CASE WHEN d_moy = 3 + THEN ws_ext_sales_price * ws_quantity + ELSE 0 END) AS mar_sales, + sum(CASE WHEN d_moy = 4 + THEN ws_ext_sales_price * ws_quantity + ELSE 0 END) AS apr_sales, + sum(CASE WHEN d_moy = 5 + THEN ws_ext_sales_price * ws_quantity + ELSE 0 END) AS may_sales, + sum(CASE WHEN d_moy = 6 + THEN ws_ext_sales_price * ws_quantity + ELSE 0 END) AS jun_sales, + sum(CASE WHEN d_moy = 7 + THEN ws_ext_sales_price * ws_quantity + ELSE 0 END) AS jul_sales, + sum(CASE WHEN d_moy = 8 + THEN ws_ext_sales_price * ws_quantity + ELSE 0 END) AS aug_sales, + sum(CASE WHEN d_moy = 9 + THEN ws_ext_sales_price * ws_quantity + ELSE 0 END) AS sep_sales, + sum(CASE WHEN d_moy = 10 + THEN ws_ext_sales_price * ws_quantity + ELSE 0 END) AS oct_sales, + sum(CASE WHEN d_moy = 11 + THEN ws_ext_sales_price * ws_quantity + ELSE 0 END) AS nov_sales, + sum(CASE WHEN d_moy = 12 + THEN ws_ext_sales_price * ws_quantity + ELSE 0 END) AS dec_sales, + sum(CASE WHEN d_moy = 1 + THEN ws_net_paid * ws_quantity + ELSE 0 END) AS jan_net, + sum(CASE WHEN d_moy = 2 + THEN ws_net_paid * ws_quantity + ELSE 0 END) AS feb_net, + sum(CASE WHEN d_moy = 3 + THEN ws_net_paid * ws_quantity + ELSE 0 END) AS mar_net, + sum(CASE WHEN d_moy = 4 + THEN ws_net_paid * ws_quantity + ELSE 0 END) AS apr_net, + sum(CASE WHEN d_moy = 5 + THEN ws_net_paid * ws_quantity + ELSE 0 END) AS may_net, + sum(CASE WHEN d_moy = 6 + THEN ws_net_paid * ws_quantity + ELSE 0 END) AS jun_net, + sum(CASE WHEN d_moy = 7 + THEN ws_net_paid * ws_quantity + ELSE 0 END) AS jul_net, + sum(CASE WHEN d_moy = 8 + THEN ws_net_paid * ws_quantity + ELSE 0 END) AS aug_net, + sum(CASE WHEN d_moy = 9 + THEN ws_net_paid * ws_quantity + ELSE 0 END) AS sep_net, + sum(CASE WHEN d_moy = 10 + THEN ws_net_paid * ws_quantity + ELSE 0 END) AS oct_net, + sum(CASE WHEN d_moy = 11 + THEN ws_net_paid * ws_quantity + ELSE 0 END) AS nov_net, + sum(CASE WHEN d_moy = 12 + THEN ws_net_paid * ws_quantity + ELSE 0 END) AS dec_net + FROM + web_sales, warehouse, date_dim, time_dim, ship_mode + WHERE + ws_warehouse_sk = w_warehouse_sk + AND ws_sold_date_sk = d_date_sk + AND ws_sold_time_sk = t_time_sk + AND ws_ship_mode_sk = sm_ship_mode_sk + AND d_year = 2001 + AND t_time BETWEEN 30838 AND 30838 + 28800 + AND sm_carrier IN ('DHL', 'BARIAN') + GROUP BY + w_warehouse_name, w_warehouse_sq_ft, w_city, w_county, w_state, w_country, d_year) + UNION ALL + (SELECT + w_warehouse_name, + w_warehouse_sq_ft, + w_city, + w_county, + w_state, + w_country, + concat('DHL', ',', 'BARIAN') AS ship_carriers, + d_year AS year, + sum(CASE WHEN d_moy = 1 + THEN cs_sales_price * cs_quantity + ELSE 0 END) AS jan_sales, + sum(CASE WHEN d_moy = 2 + THEN cs_sales_price * cs_quantity + ELSE 0 END) AS feb_sales, + sum(CASE WHEN d_moy = 3 + THEN cs_sales_price * cs_quantity + ELSE 0 END) AS mar_sales, + sum(CASE WHEN d_moy = 4 + THEN cs_sales_price * cs_quantity + ELSE 0 END) AS apr_sales, + sum(CASE WHEN d_moy = 5 + THEN cs_sales_price * cs_quantity + ELSE 0 END) AS may_sales, + sum(CASE WHEN d_moy = 6 + THEN cs_sales_price * cs_quantity + ELSE 0 END) AS jun_sales, + sum(CASE WHEN d_moy = 7 + THEN cs_sales_price * cs_quantity + ELSE 0 END) AS jul_sales, + sum(CASE WHEN d_moy = 8 + THEN cs_sales_price * cs_quantity + ELSE 0 END) AS aug_sales, + sum(CASE WHEN d_moy = 9 + THEN cs_sales_price * cs_quantity + ELSE 0 END) AS sep_sales, + sum(CASE WHEN d_moy = 10 + THEN cs_sales_price * cs_quantity + ELSE 0 END) AS oct_sales, + sum(CASE WHEN d_moy = 11 + THEN cs_sales_price * cs_quantity + ELSE 0 END) AS nov_sales, + sum(CASE WHEN d_moy = 12 + THEN cs_sales_price * cs_quantity + ELSE 0 END) AS dec_sales, + sum(CASE WHEN d_moy = 1 + THEN cs_net_paid_inc_tax * cs_quantity + ELSE 0 END) AS jan_net, + sum(CASE WHEN d_moy = 2 + THEN cs_net_paid_inc_tax * cs_quantity + ELSE 0 END) AS feb_net, + sum(CASE WHEN d_moy = 3 + THEN cs_net_paid_inc_tax * cs_quantity + ELSE 0 END) AS mar_net, + sum(CASE WHEN d_moy = 4 + THEN cs_net_paid_inc_tax * cs_quantity + ELSE 0 END) AS apr_net, + sum(CASE WHEN d_moy = 5 + THEN cs_net_paid_inc_tax * cs_quantity + ELSE 0 END) AS may_net, + sum(CASE WHEN d_moy = 6 + THEN cs_net_paid_inc_tax * cs_quantity + ELSE 0 END) AS jun_net, + sum(CASE WHEN d_moy = 7 + THEN cs_net_paid_inc_tax * cs_quantity + ELSE 0 END) AS jul_net, + sum(CASE WHEN d_moy = 8 + THEN cs_net_paid_inc_tax * cs_quantity + ELSE 0 END) AS aug_net, + sum(CASE WHEN d_moy = 9 + THEN cs_net_paid_inc_tax * cs_quantity + ELSE 0 END) AS sep_net, + sum(CASE WHEN d_moy = 10 + THEN cs_net_paid_inc_tax * cs_quantity + ELSE 0 END) AS oct_net, + sum(CASE WHEN d_moy = 11 + THEN cs_net_paid_inc_tax * cs_quantity + ELSE 0 END) AS nov_net, + sum(CASE WHEN d_moy = 12 + THEN cs_net_paid_inc_tax * cs_quantity + ELSE 0 END) AS dec_net + FROM + catalog_sales, warehouse, date_dim, time_dim, ship_mode + WHERE + cs_warehouse_sk = w_warehouse_sk + AND cs_sold_date_sk = d_date_sk + AND cs_sold_time_sk = t_time_sk + AND cs_ship_mode_sk = sm_ship_mode_sk + AND d_year = 2001 + AND t_time BETWEEN 30838 AND 30838 + 28800 + AND sm_carrier IN ('DHL', 'BARIAN') + GROUP BY + w_warehouse_name, w_warehouse_sq_ft, w_city, w_county, w_state, w_country, d_year + ) + ) x +GROUP BY + w_warehouse_name, w_warehouse_sq_ft, w_city, w_county, w_state, w_country, + ship_carriers, year +ORDER BY w_warehouse_name +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q67.sql b/athena-tpcds/src/main/resources/queries/q67.sql new file mode 100644 index 0000000000..f66e2252bd --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q67.sql @@ -0,0 +1,38 @@ +SELECT * +FROM + (SELECT + i_category, + i_class, + i_brand, + i_product_name, + d_year, + d_qoy, + d_moy, + s_store_id, + sumsales, + rank() + OVER (PARTITION BY i_category + ORDER BY sumsales DESC) rk + FROM + (SELECT + i_category, + i_class, + i_brand, + i_product_name, + d_year, + d_qoy, + d_moy, + s_store_id, + sum(coalesce(ss_sales_price * ss_quantity, 0)) sumsales + FROM store_sales, date_dim, store, item + WHERE ss_sold_date_sk = d_date_sk + AND ss_item_sk = i_item_sk + AND ss_store_sk = s_store_sk + AND d_month_seq BETWEEN 1200 AND 1200 + 11 + GROUP BY ROLLUP (i_category, i_class, i_brand, i_product_name, d_year, d_qoy, + d_moy, s_store_id)) dw1) dw2 +WHERE rk <= 100 +ORDER BY + i_category, i_class, i_brand, i_product_name, d_year, + d_qoy, d_moy, s_store_id, sumsales, rk +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q68.sql b/athena-tpcds/src/main/resources/queries/q68.sql new file mode 100644 index 0000000000..adb8a7189d --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q68.sql @@ -0,0 +1,34 @@ +SELECT + c_last_name, + c_first_name, + ca_city, + bought_city, + ss_ticket_number, + extended_price, + extended_tax, + list_price +FROM (SELECT + ss_ticket_number, + ss_customer_sk, + ca_city bought_city, + sum(ss_ext_sales_price) extended_price, + sum(ss_ext_list_price) list_price, + sum(ss_ext_tax) extended_tax +FROM store_sales, date_dim, store, household_demographics, customer_address +WHERE store_sales.ss_sold_date_sk = date_dim.d_date_sk + AND store_sales.ss_store_sk = store.s_store_sk + AND store_sales.ss_hdemo_sk = household_demographics.hd_demo_sk + AND store_sales.ss_addr_sk = customer_address.ca_address_sk + AND date_dim.d_dom BETWEEN 1 AND 2 + AND (household_demographics.hd_dep_count = 4 OR + household_demographics.hd_vehicle_count = 3) + AND date_dim.d_year IN (1999, 1999 + 1, 1999 + 2) + AND store.s_city IN ('Midway', 'Fairview') +GROUP BY ss_ticket_number, ss_customer_sk, ss_addr_sk, ca_city) dn, + customer, + customer_address current_addr +WHERE ss_customer_sk = c_customer_sk + AND customer.c_current_addr_sk = current_addr.ca_address_sk + AND current_addr.ca_city <> bought_city +ORDER BY c_last_name, ss_ticket_number +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q69.sql b/athena-tpcds/src/main/resources/queries/q69.sql new file mode 100644 index 0000000000..1f0ee64f56 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q69.sql @@ -0,0 +1,38 @@ +SELECT + cd_gender, + cd_marital_status, + cd_education_status, + count(*) cnt1, + cd_purchase_estimate, + count(*) cnt2, + cd_credit_rating, + count(*) cnt3 +FROM + customer c, customer_address ca, customer_demographics +WHERE + c.c_current_addr_sk = ca.ca_address_sk AND + ca_state IN ('KY', 'GA', 'NM') AND + cd_demo_sk = c.c_current_cdemo_sk AND + exists(SELECT * + FROM store_sales, date_dim + WHERE c.c_customer_sk = ss_customer_sk AND + ss_sold_date_sk = d_date_sk AND + d_year = 2001 AND + d_moy BETWEEN 4 AND 4 + 2) AND + (NOT exists(SELECT * + FROM web_sales, date_dim + WHERE c.c_customer_sk = ws_bill_customer_sk AND + ws_sold_date_sk = d_date_sk AND + d_year = 2001 AND + d_moy BETWEEN 4 AND 4 + 2) AND + NOT exists(SELECT * + FROM catalog_sales, date_dim + WHERE c.c_customer_sk = cs_ship_customer_sk AND + cs_sold_date_sk = d_date_sk AND + d_year = 2001 AND + d_moy BETWEEN 4 AND 4 + 2)) +GROUP BY cd_gender, cd_marital_status, cd_education_status, + cd_purchase_estimate, cd_credit_rating +ORDER BY cd_gender, cd_marital_status, cd_education_status, + cd_purchase_estimate, cd_credit_rating +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q7.sql b/athena-tpcds/src/main/resources/queries/q7.sql new file mode 100644 index 0000000000..39144f13c8 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q7.sql @@ -0,0 +1,19 @@ +SELECT + i_item_id, + avg(ss_quantity) agg1, + avg(ss_list_price) agg2, + avg(ss_coupon_amt) agg3, + avg(ss_sales_price) agg4 +FROM store_sales[ TABLE_SUFFIX ], customer_demographics[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ], item[ TABLE_SUFFIX ], promotion[ TABLE_SUFFIX ] +WHERE ss_sold_date_sk = d_date_sk AND + ss_item_sk = i_item_sk AND + ss_cdemo_sk = cd_demo_sk AND + ss_promo_sk = p_promo_sk AND + cd_gender = 'M' AND + cd_marital_status = 'S' AND + cd_education_status = 'College' AND + (p_channel_email = 'N' OR p_channel_event = 'N') AND + d_year = 2000 +GROUP BY i_item_id +ORDER BY i_item_id +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q71.sql b/athena-tpcds/src/main/resources/queries/q71.sql new file mode 100644 index 0000000000..8d724b9244 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q71.sql @@ -0,0 +1,44 @@ +SELECT + i_brand_id brand_id, + i_brand brand, + t_hour, + t_minute, + sum(ext_price) ext_price +FROM item, + (SELECT + ws_ext_sales_price AS ext_price, + ws_sold_date_sk AS sold_date_sk, + ws_item_sk AS sold_item_sk, + ws_sold_time_sk AS time_sk + FROM web_sales, date_dim + WHERE d_date_sk = ws_sold_date_sk + AND d_moy = 11 + AND d_year = 1999 + UNION ALL + SELECT + cs_ext_sales_price AS ext_price, + cs_sold_date_sk AS sold_date_sk, + cs_item_sk AS sold_item_sk, + cs_sold_time_sk AS time_sk + FROM catalog_sales, date_dim + WHERE d_date_sk = cs_sold_date_sk + AND d_moy = 11 + AND d_year = 1999 + UNION ALL + SELECT + ss_ext_sales_price AS ext_price, + ss_sold_date_sk AS sold_date_sk, + ss_item_sk AS sold_item_sk, + ss_sold_time_sk AS time_sk + FROM store_sales, date_dim + WHERE d_date_sk = ss_sold_date_sk + AND d_moy = 11 + AND d_year = 1999 + ) AS tmp, time_dim +WHERE + sold_item_sk = i_item_sk + AND i_manager_id = 1 + AND time_sk = t_time_sk + AND (t_meal_time = 'breakfast' OR t_meal_time = 'dinner') +GROUP BY i_brand, i_brand_id, t_hour, t_minute +ORDER BY ext_price DESC, brand_id diff --git a/athena-tpcds/src/main/resources/queries/q73.sql b/athena-tpcds/src/main/resources/queries/q73.sql new file mode 100644 index 0000000000..881be2e902 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q73.sql @@ -0,0 +1,30 @@ +SELECT + c_last_name, + c_first_name, + c_salutation, + c_preferred_cust_flag, + ss_ticket_number, + cnt +FROM + (SELECT + ss_ticket_number, + ss_customer_sk, + count(*) cnt + FROM store_sales, date_dim, store, household_demographics + WHERE store_sales.ss_sold_date_sk = date_dim.d_date_sk + AND store_sales.ss_store_sk = store.s_store_sk + AND store_sales.ss_hdemo_sk = household_demographics.hd_demo_sk + AND date_dim.d_dom BETWEEN 1 AND 2 + AND (household_demographics.hd_buy_potential = '>10000' OR + household_demographics.hd_buy_potential = 'unknown') + AND household_demographics.hd_vehicle_count > 0 + AND CASE WHEN household_demographics.hd_vehicle_count > 0 + THEN + household_demographics.hd_dep_count / household_demographics.hd_vehicle_count + ELSE NULL END > 1 + AND date_dim.d_year IN (1999, 1999 + 1, 1999 + 2) + AND store.s_county IN ('Williamson County', 'Franklin Parish', 'Bronx County', 'Orange County') + GROUP BY ss_ticket_number, ss_customer_sk) dj, customer +WHERE ss_customer_sk = c_customer_sk + AND cnt BETWEEN 1 AND 5 +ORDER BY cnt DESC diff --git a/athena-tpcds/src/main/resources/queries/q74.sql b/athena-tpcds/src/main/resources/queries/q74.sql new file mode 100644 index 0000000000..154b26d680 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q74.sql @@ -0,0 +1,58 @@ +WITH year_total AS ( + SELECT + c_customer_id customer_id, + c_first_name customer_first_name, + c_last_name customer_last_name, + d_year AS year, + sum(ss_net_paid) year_total, + 's' sale_type + FROM + customer, store_sales, date_dim + WHERE c_customer_sk = ss_customer_sk + AND ss_sold_date_sk = d_date_sk + AND d_year IN (2001, 2001 + 1) + GROUP BY + c_customer_id, c_first_name, c_last_name, d_year + UNION ALL + SELECT + c_customer_id customer_id, + c_first_name customer_first_name, + c_last_name customer_last_name, + d_year AS year, + sum(ws_net_paid) year_total, + 'w' sale_type + FROM + customer, web_sales, date_dim + WHERE c_customer_sk = ws_bill_customer_sk + AND ws_sold_date_sk = d_date_sk + AND d_year IN (2001, 2001 + 1) + GROUP BY + c_customer_id, c_first_name, c_last_name, d_year) +SELECT + t_s_secyear.customer_id, + t_s_secyear.customer_first_name, + t_s_secyear.customer_last_name +FROM + year_total t_s_firstyear, year_total t_s_secyear, + year_total t_w_firstyear, year_total t_w_secyear +WHERE t_s_secyear.customer_id = t_s_firstyear.customer_id + AND t_s_firstyear.customer_id = t_w_secyear.customer_id + AND t_s_firstyear.customer_id = t_w_firstyear.customer_id + AND t_s_firstyear.sale_type = 's' + AND t_w_firstyear.sale_type = 'w' + AND t_s_secyear.sale_type = 's' + AND t_w_secyear.sale_type = 'w' + AND t_s_firstyear.year = 2001 + AND t_s_secyear.year = 2001 + 1 + AND t_w_firstyear.year = 2001 + AND t_w_secyear.year = 2001 + 1 + AND t_s_firstyear.year_total > 0 + AND t_w_firstyear.year_total > 0 + AND CASE WHEN t_w_firstyear.year_total > 0 + THEN t_w_secyear.year_total / t_w_firstyear.year_total + ELSE NULL END + > CASE WHEN t_s_firstyear.year_total > 0 + THEN t_s_secyear.year_total / t_s_firstyear.year_total + ELSE NULL END +ORDER BY 1, 1, 1 +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q75.sql b/athena-tpcds/src/main/resources/queries/q75.sql new file mode 100644 index 0000000000..2a143232b5 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q75.sql @@ -0,0 +1,76 @@ +WITH all_sales AS ( + SELECT + d_year, + i_brand_id, + i_class_id, + i_category_id, + i_manufact_id, + SUM(sales_cnt) AS sales_cnt, + SUM(sales_amt) AS sales_amt + FROM ( + SELECT + d_year, + i_brand_id, + i_class_id, + i_category_id, + i_manufact_id, + cs_quantity - COALESCE(cr_return_quantity, 0) AS sales_cnt, + cs_ext_sales_price - COALESCE(cr_return_amount, 0.0) AS sales_amt + FROM catalog_sales + JOIN item ON i_item_sk = cs_item_sk + JOIN date_dim ON d_date_sk = cs_sold_date_sk + LEFT JOIN catalog_returns ON (cs_order_number = cr_order_number + AND cs_item_sk = cr_item_sk) + WHERE i_category = 'Books' + UNION + SELECT + d_year, + i_brand_id, + i_class_id, + i_category_id, + i_manufact_id, + ss_quantity - COALESCE(sr_return_quantity, 0) AS sales_cnt, + ss_ext_sales_price - COALESCE(sr_return_amt, 0.0) AS sales_amt + FROM store_sales + JOIN item ON i_item_sk = ss_item_sk + JOIN date_dim ON d_date_sk = ss_sold_date_sk + LEFT JOIN store_returns ON (ss_ticket_number = sr_ticket_number + AND ss_item_sk = sr_item_sk) + WHERE i_category = 'Books' + UNION + SELECT + d_year, + i_brand_id, + i_class_id, + i_category_id, + i_manufact_id, + ws_quantity - COALESCE(wr_return_quantity, 0) AS sales_cnt, + ws_ext_sales_price - COALESCE(wr_return_amt, 0.0) AS sales_amt + FROM web_sales + JOIN item ON i_item_sk = ws_item_sk + JOIN date_dim ON d_date_sk = ws_sold_date_sk + LEFT JOIN web_returns ON (ws_order_number = wr_order_number + AND ws_item_sk = wr_item_sk) + WHERE i_category = 'Books') sales_detail + GROUP BY d_year, i_brand_id, i_class_id, i_category_id, i_manufact_id) +SELECT + prev_yr.d_year AS prev_year, + curr_yr.d_year AS year, + curr_yr.i_brand_id, + curr_yr.i_class_id, + curr_yr.i_category_id, + curr_yr.i_manufact_id, + prev_yr.sales_cnt AS prev_yr_cnt, + curr_yr.sales_cnt AS curr_yr_cnt, + curr_yr.sales_cnt - prev_yr.sales_cnt AS sales_cnt_diff, + curr_yr.sales_amt - prev_yr.sales_amt AS sales_amt_diff +FROM all_sales curr_yr, all_sales prev_yr +WHERE curr_yr.i_brand_id = prev_yr.i_brand_id + AND curr_yr.i_class_id = prev_yr.i_class_id + AND curr_yr.i_category_id = prev_yr.i_category_id + AND curr_yr.i_manufact_id = prev_yr.i_manufact_id + AND curr_yr.d_year = 2002 + AND prev_yr.d_year = 2002 - 1 + AND CAST(curr_yr.sales_cnt AS DECIMAL(17, 2)) / CAST(prev_yr.sales_cnt AS DECIMAL(17, 2)) < 0.9 +ORDER BY sales_cnt_diff +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q76.sql b/athena-tpcds/src/main/resources/queries/q76.sql new file mode 100644 index 0000000000..815fa922be --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q76.sql @@ -0,0 +1,47 @@ +SELECT + channel, + col_name, + d_year, + d_qoy, + i_category, + COUNT(*) sales_cnt, + SUM(ext_sales_price) sales_amt +FROM ( + SELECT + 'store' AS channel, + ss_store_sk col_name, + d_year, + d_qoy, + i_category, + ss_ext_sales_price ext_sales_price + FROM store_sales, item, date_dim + WHERE ss_store_sk IS NULL + AND ss_sold_date_sk = d_date_sk + AND ss_item_sk = i_item_sk + UNION ALL + SELECT + 'web' AS channel, + ws_ship_customer_sk col_name, + d_year, + d_qoy, + i_category, + ws_ext_sales_price ext_sales_price + FROM web_sales, item, date_dim + WHERE ws_ship_customer_sk IS NULL + AND ws_sold_date_sk = d_date_sk + AND ws_item_sk = i_item_sk + UNION ALL + SELECT + 'catalog' AS channel, + cs_ship_addr_sk col_name, + d_year, + d_qoy, + i_category, + cs_ext_sales_price ext_sales_price + FROM catalog_sales, item, date_dim + WHERE cs_ship_addr_sk IS NULL + AND cs_sold_date_sk = d_date_sk + AND cs_item_sk = i_item_sk) foo +GROUP BY channel, col_name, d_year, d_qoy, i_category +ORDER BY channel, col_name, d_year, d_qoy, i_category +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q77.sql b/athena-tpcds/src/main/resources/queries/q77.sql new file mode 100644 index 0000000000..5ce4e91b8c --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q77.sql @@ -0,0 +1,100 @@ +WITH ss AS +(SELECT + s_store_sk, + sum(ss_ext_sales_price) AS sales, + sum(ss_net_profit) AS profit + FROM store_sales, date_dim, store + WHERE ss_sold_date_sk = d_date_sk + AND CAST(d_date as DATE) BETWEEN cast('2000-08-03' AS DATE) AND + (cast('2000-08-03' AS DATE) + INTERVAL '30' day) + AND ss_store_sk = s_store_sk + GROUP BY s_store_sk), + sr AS + (SELECT + s_store_sk, + sum(sr_return_amt) AS returns, + sum(sr_net_loss) AS profit_loss + FROM store_returns, date_dim, store + WHERE sr_returned_date_sk = d_date_sk + AND CAST(d_date as DATE) BETWEEN cast('2000-08-03' AS DATE) AND + (cast('2000-08-03' AS DATE) + INTERVAL '30' day) + AND sr_store_sk = s_store_sk + GROUP BY s_store_sk), + cs AS + (SELECT + cs_call_center_sk, + sum(cs_ext_sales_price) AS sales, + sum(cs_net_profit) AS profit + FROM catalog_sales, date_dim + WHERE cs_sold_date_sk = d_date_sk + AND CAST(d_date as DATE) BETWEEN cast('2000-08-03' AS DATE) AND + (cast('2000-08-03' AS DATE) + INTERVAL '30' day) + GROUP BY cs_call_center_sk), + cr AS + (SELECT + sum(cr_return_amount) AS returns, + sum(cr_net_loss) AS profit_loss + FROM catalog_returns, date_dim + WHERE cr_returned_date_sk = d_date_sk + AND CAST(d_date as DATE) BETWEEN cast('2000-08-03' AS DATE) AND + (cast('2000-08-03' AS DATE) + INTERVAL '30' day)), + ws AS + (SELECT + wp_web_page_sk, + sum(ws_ext_sales_price) AS sales, + sum(ws_net_profit) AS profit + FROM web_sales, date_dim, web_page + WHERE ws_sold_date_sk = d_date_sk + AND CAST(d_date as DATE) BETWEEN cast('2000-08-03' AS DATE) AND + (cast('2000-08-03' AS DATE) + INTERVAL '30' day) + AND ws_web_page_sk = wp_web_page_sk + GROUP BY wp_web_page_sk), + wr AS + (SELECT + wp_web_page_sk, + sum(wr_return_amt) AS returns, + sum(wr_net_loss) AS profit_loss + FROM web_returns, date_dim, web_page + WHERE wr_returned_date_sk = d_date_sk + AND CAST(d_date as DATE) BETWEEN cast('2000-08-03' AS DATE) AND + (cast('2000-08-03' AS DATE) + INTERVAL '30' day) + AND wr_web_page_sk = wp_web_page_sk + GROUP BY wp_web_page_sk) +SELECT + channel, + id, + sum(sales) AS sales, + sum(returns) AS returns, + sum(profit) AS profit +FROM + (SELECT + 'store channel' AS channel, + ss.s_store_sk AS id, + sales, + coalesce(returns, 0) AS returns, + (profit - coalesce(profit_loss, 0)) AS profit + FROM ss + LEFT JOIN sr + ON ss.s_store_sk = sr.s_store_sk + UNION ALL + SELECT + 'catalog channel' AS channel, + cs_call_center_sk AS id, + sales, + returns, + (profit - profit_loss) AS profit + FROM cs, cr + UNION ALL + SELECT + 'web channel' AS channel, + ws.wp_web_page_sk AS id, + sales, + coalesce(returns, 0) returns, + (profit - coalesce(profit_loss, 0)) AS profit + FROM ws + LEFT JOIN wr + ON ws.wp_web_page_sk = wr.wp_web_page_sk + ) x +GROUP BY ROLLUP (channel, id) +ORDER BY channel, id +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q78.sql b/athena-tpcds/src/main/resources/queries/q78.sql new file mode 100644 index 0000000000..07b0940e26 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q78.sql @@ -0,0 +1,64 @@ +WITH ws AS +(SELECT + d_year AS ws_sold_year, + ws_item_sk, + ws_bill_customer_sk ws_customer_sk, + sum(ws_quantity) ws_qty, + sum(ws_wholesale_cost) ws_wc, + sum(ws_sales_price) ws_sp + FROM web_sales + LEFT JOIN web_returns ON wr_order_number = ws_order_number AND ws_item_sk = wr_item_sk + JOIN date_dim ON ws_sold_date_sk = d_date_sk + WHERE wr_order_number IS NULL + GROUP BY d_year, ws_item_sk, ws_bill_customer_sk +), + cs AS + (SELECT + d_year AS cs_sold_year, + cs_item_sk, + cs_bill_customer_sk cs_customer_sk, + sum(cs_quantity) cs_qty, + sum(cs_wholesale_cost) cs_wc, + sum(cs_sales_price) cs_sp + FROM catalog_sales + LEFT JOIN catalog_returns ON cr_order_number = cs_order_number AND cs_item_sk = cr_item_sk + JOIN date_dim ON cs_sold_date_sk = d_date_sk + WHERE cr_order_number IS NULL + GROUP BY d_year, cs_item_sk, cs_bill_customer_sk + ), + ss AS + (SELECT + d_year AS ss_sold_year, + ss_item_sk, + ss_customer_sk, + sum(ss_quantity) ss_qty, + sum(ss_wholesale_cost) ss_wc, + sum(ss_sales_price) ss_sp + FROM store_sales + LEFT JOIN store_returns ON sr_ticket_number = ss_ticket_number AND ss_item_sk = sr_item_sk + JOIN date_dim ON ss_sold_date_sk = d_date_sk + WHERE sr_ticket_number IS NULL + GROUP BY d_year, ss_item_sk, ss_customer_sk + ) +SELECT + round(ss_qty / (coalesce(ws_qty + cs_qty, 1)), 2) ratio, + ss_qty store_qty, + ss_wc store_wholesale_cost, + ss_sp store_sales_price, + coalesce(ws_qty, 0) + coalesce(cs_qty, 0) other_chan_qty, + coalesce(ws_wc, 0) + coalesce(cs_wc, 0) other_chan_wholesale_cost, + coalesce(ws_sp, 0) + coalesce(cs_sp, 0) other_chan_sales_price +FROM ss + LEFT JOIN ws + ON (ws_sold_year = ss_sold_year AND ws_item_sk = ss_item_sk AND ws_customer_sk = ss_customer_sk) + LEFT JOIN cs + ON (cs_sold_year = ss_sold_year AND cs_item_sk = ss_item_sk AND cs_customer_sk = ss_customer_sk) +WHERE coalesce(ws_qty, 0) > 0 AND coalesce(cs_qty, 0) > 0 AND ss_sold_year = 2000 +ORDER BY + ratio, + ss_qty DESC, ss_wc DESC, ss_sp DESC, + other_chan_qty, + other_chan_wholesale_cost, + other_chan_sales_price, + round(ss_qty / (coalesce(ws_qty + cs_qty, 1)), 2) +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q79.sql b/athena-tpcds/src/main/resources/queries/q79.sql new file mode 100644 index 0000000000..08f86dc203 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q79.sql @@ -0,0 +1,27 @@ +SELECT + c_last_name, + c_first_name, + substr(s_city, 1, 30), + ss_ticket_number, + amt, + profit +FROM + (SELECT + ss_ticket_number, + ss_customer_sk, + store.s_city, + sum(ss_coupon_amt) amt, + sum(ss_net_profit) profit + FROM store_sales, date_dim, store, household_demographics + WHERE store_sales.ss_sold_date_sk = date_dim.d_date_sk + AND store_sales.ss_store_sk = store.s_store_sk + AND store_sales.ss_hdemo_sk = household_demographics.hd_demo_sk + AND (household_demographics.hd_dep_count = 6 OR + household_demographics.hd_vehicle_count > 2) + AND date_dim.d_dow = 1 + AND date_dim.d_year IN (1999, 1999 + 1, 1999 + 2) + AND store.s_number_employees BETWEEN 200 AND 295 + GROUP BY ss_ticket_number, ss_customer_sk, ss_addr_sk, store.s_city) ms, customer +WHERE ss_customer_sk = c_customer_sk +ORDER BY c_last_name, c_first_name, substr(s_city, 1, 30), profit +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q8.sql b/athena-tpcds/src/main/resources/queries/q8.sql new file mode 100644 index 0000000000..9b31324225 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q8.sql @@ -0,0 +1,87 @@ +SELECT + s_store_name, + sum(ss_net_profit) +FROM store_sales[ TABLE_SUFFIX ], date_dim[ TABLE_SUFFIX ], store[ TABLE_SUFFIX ], + (SELECT ca_zip + FROM ( + (SELECT substr(ca_zip, 1, 5) ca_zip + FROM customer_address[ TABLE_SUFFIX ] + WHERE substr(ca_zip, 1, 5) IN ( + '24128','76232','65084','87816','83926','77556','20548', + '26231','43848','15126','91137','61265','98294','25782', + '17920','18426','98235','40081','84093','28577','55565', + '17183','54601','67897','22752','86284','18376','38607', + '45200','21756','29741','96765','23932','89360','29839', + '25989','28898','91068','72550','10390','18845','47770', + '82636','41367','76638','86198','81312','37126','39192', + '88424','72175','81426','53672','10445','42666','66864', + '66708','41248','48583','82276','18842','78890','49448', + '14089','38122','34425','79077','19849','43285','39861', + '66162','77610','13695','99543','83444','83041','12305', + '57665','68341','25003','57834','62878','49130','81096', + '18840','27700','23470','50412','21195','16021','76107', + '71954','68309','18119','98359','64544','10336','86379', + '27068','39736','98569','28915','24206','56529','57647', + '54917','42961','91110','63981','14922','36420','23006', + '67467','32754','30903','20260','31671','51798','72325', + '85816','68621','13955','36446','41766','68806','16725', + '15146','22744','35850','88086','51649','18270','52867', + '39972','96976','63792','11376','94898','13595','10516', + '90225','58943','39371','94945','28587','96576','57855', + '28488','26105','83933','25858','34322','44438','73171', + '30122','34102','22685','71256','78451','54364','13354', + '45375','40558','56458','28286','45266','47305','69399', + '83921','26233','11101','15371','69913','35942','15882', + '25631','24610','44165','99076','33786','70738','26653', + '14328','72305','62496','22152','10144','64147','48425', + '14663','21076','18799','30450','63089','81019','68893', + '24996','51200','51211','45692','92712','70466','79994', + '22437','25280','38935','71791','73134','56571','14060', + '19505','72425','56575','74351','68786','51650','20004', + '18383','76614','11634','18906','15765','41368','73241', + '76698','78567','97189','28545','76231','75691','22246', + '51061','90578','56691','68014','51103','94167','57047', + '14867','73520','15734','63435','25733','35474','24676', + '94627','53535','17879','15559','53268','59166','11928', + '59402','33282','45721','43933','68101','33515','36634', + '71286','19736','58058','55253','67473','41918','19515', + '36495','19430','22351','77191','91393','49156','50298', + '87501','18652','53179','18767','63193','23968','65164', + '68880','21286','72823','58470','67301','13394','31016', + '70372','67030','40604','24317','45748','39127','26065', + '77721','31029','31880','60576','24671','45549','13376', + '50016','33123','19769','22927','97789','46081','72151', + '15723','46136','51949','68100','96888','64528','14171', + '79777','28709','11489','25103','32213','78668','22245', + '15798','27156','37930','62971','21337','51622','67853', + '10567','38415','15455','58263','42029','60279','37125', + '56240','88190','50308','26859','64457','89091','82136', + '62377','36233','63837','58078','17043','30010','60099', + '28810','98025','29178','87343','73273','30469','64034', + '39516','86057','21309','90257','67875','40162','11356', + '73650','61810','72013','30431','22461','19512','13375', + '55307','30625','83849','68908','26689','96451','38193', + '46820','88885','84935','69035','83144','47537','56616', + '94983','48033','69952','25486','61547','27385','61860', + '58048','56910','16807','17871','35258','31387','35458', + '35576')) + INTERSECT + (SELECT ca_zip + FROM + (SELECT + substr(ca_zip, 1, 5) ca_zip, + count(*) cnt + FROM customer_address[ TABLE_SUFFIX ], customer[ TABLE_SUFFIX ] + WHERE ca_address_sk = c_current_addr_sk AND + c_preferred_cust_flag = 'Y' + GROUP BY ca_zip + HAVING count(*) > 10) A1) + ) A2 + ) V1 +WHERE ss_store_sk = s_store_sk + AND ss_sold_date_sk = d_date_sk + AND d_qoy = 2 AND d_year = 1998 + AND (substr(s_zip, 1, 2) = substr(V1.ca_zip, 1, 2)) +GROUP BY s_store_name +ORDER BY s_store_name +LIMIT 100 \ No newline at end of file diff --git a/athena-tpcds/src/main/resources/queries/q80.sql b/athena-tpcds/src/main/resources/queries/q80.sql new file mode 100644 index 0000000000..cd4b80ada4 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q80.sql @@ -0,0 +1,94 @@ +WITH ssr AS +(SELECT + s_store_id AS store_id, + sum(ss_ext_sales_price) AS sales, + sum(coalesce(sr_return_amt, 0)) AS returns, + sum(ss_net_profit - coalesce(sr_net_loss, 0)) AS profit + FROM store_sales + LEFT OUTER JOIN store_returns ON + (ss_item_sk = sr_item_sk AND + ss_ticket_number = sr_ticket_number) + , + date_dim, store, item, promotion + WHERE ss_sold_date_sk = d_date_sk + AND CAST(d_date as DATE) BETWEEN cast('2000-08-23' AS DATE) + AND (cast('2000-08-23' AS DATE) + INTERVAL '30' day) + AND ss_store_sk = s_store_sk + AND ss_item_sk = i_item_sk + AND i_current_price > 50 + AND ss_promo_sk = p_promo_sk + AND p_channel_tv = 'N' + GROUP BY s_store_id), + csr AS + (SELECT + cp_catalog_page_id AS catalog_page_id, + sum(cs_ext_sales_price) AS sales, + sum(coalesce(cr_return_amount, 0)) AS returns, + sum(cs_net_profit - coalesce(cr_net_loss, 0)) AS profit + FROM catalog_sales + LEFT OUTER JOIN catalog_returns ON + (cs_item_sk = cr_item_sk AND + cs_order_number = cr_order_number) + , + date_dim, catalog_page, item, promotion + WHERE cs_sold_date_sk = d_date_sk + AND CAST(d_date as DATE) BETWEEN cast('2000-08-23' AS DATE) + AND (cast('2000-08-23' AS DATE) + INTERVAL '30' day) + AND cs_catalog_page_sk = cp_catalog_page_sk + AND cs_item_sk = i_item_sk + AND i_current_price > 50 + AND cs_promo_sk = p_promo_sk + AND p_channel_tv = 'N' + GROUP BY cp_catalog_page_id), + wsr AS + (SELECT + web_site_id, + sum(ws_ext_sales_price) AS sales, + sum(coalesce(wr_return_amt, 0)) AS returns, + sum(ws_net_profit - coalesce(wr_net_loss, 0)) AS profit + FROM web_sales + LEFT OUTER JOIN web_returns ON + (ws_item_sk = wr_item_sk AND ws_order_number = wr_order_number) + , + date_dim, web_site, item, promotion + WHERE ws_sold_date_sk = d_date_sk + AND CAST(d_date as DATE) BETWEEN cast('2000-08-23' AS DATE) + AND (cast('2000-08-23' AS DATE) + INTERVAL '30' day) + AND ws_web_site_sk = web_site_sk + AND ws_item_sk = i_item_sk + AND i_current_price > 50 + AND ws_promo_sk = p_promo_sk + AND p_channel_tv = 'N' + GROUP BY web_site_id) +SELECT + channel, + id, + sum(sales) AS sales, + sum(returns) AS returns, + sum(profit) AS profit +FROM (SELECT + 'store channel' AS channel, + concat('store', store_id) AS id, + sales, + returns, + profit + FROM ssr + UNION ALL + SELECT + 'catalog channel' AS channel, + concat('catalog_page', catalog_page_id) AS id, + sales, + returns, + profit + FROM csr + UNION ALL + SELECT + 'web channel' AS channel, + concat('web_site', web_site_id) AS id, + sales, + returns, + profit + FROM wsr) x +GROUP BY ROLLUP (channel, id) +ORDER BY channel, id +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q81.sql b/athena-tpcds/src/main/resources/queries/q81.sql new file mode 100644 index 0000000000..18f0ffa7e8 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q81.sql @@ -0,0 +1,38 @@ +WITH customer_total_return AS +(SELECT + cr_returning_customer_sk AS ctr_customer_sk, + ca_state AS ctr_state, + sum(cr_return_amt_inc_tax) AS ctr_total_return + FROM catalog_returns, date_dim, customer_address + WHERE cr_returned_date_sk = d_date_sk + AND d_year = 2000 + AND cr_returning_addr_sk = ca_address_sk + GROUP BY cr_returning_customer_sk, ca_state ) +SELECT + c_customer_id, + c_salutation, + c_first_name, + c_last_name, + ca_street_number, + ca_street_name, + ca_street_type, + ca_suite_number, + ca_city, + ca_county, + ca_state, + ca_zip, + ca_country, + ca_gmt_offset, + ca_location_type, + ctr_total_return +FROM customer_total_return ctr1, customer_address, customer +WHERE ctr1.ctr_total_return > (SELECT avg(ctr_total_return) * 1.2 +FROM customer_total_return ctr2 +WHERE ctr1.ctr_state = ctr2.ctr_state) + AND ca_address_sk = c_current_addr_sk + AND ca_state = 'GA' + AND ctr1.ctr_customer_sk = c_customer_sk +ORDER BY c_customer_id, c_salutation, c_first_name, c_last_name, ca_street_number, ca_street_name + , ca_street_type, ca_suite_number, ca_city, ca_county, ca_state, ca_zip, ca_country, ca_gmt_offset + , ca_location_type, ctr_total_return +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q82.sql b/athena-tpcds/src/main/resources/queries/q82.sql new file mode 100644 index 0000000000..185eb6911b --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q82.sql @@ -0,0 +1,15 @@ +SELECT + i_item_id, + i_item_desc, + i_current_price +FROM item, inventory, date_dim, store_sales +WHERE i_current_price BETWEEN 62 AND 62 + 30 + AND inv_item_sk = i_item_sk + AND d_date_sk = inv_date_sk + AND CAST(d_date as DATE) BETWEEN cast('2000-05-25' AS DATE) AND (cast('2000-05-25' AS DATE) + INTERVAL '60' day) + AND i_manufact_id IN (129, 270, 821, 423) + AND inv_quantity_on_hand BETWEEN 100 AND 500 + AND ss_item_sk = i_item_sk +GROUP BY i_item_id, i_item_desc, i_current_price +ORDER BY i_item_id +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q83.sql b/athena-tpcds/src/main/resources/queries/q83.sql new file mode 100644 index 0000000000..988d26006e --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q83.sql @@ -0,0 +1,56 @@ +WITH sr_items AS +(SELECT + i_item_id item_id, + sum(sr_return_quantity) sr_item_qty + FROM store_returns, item, date_dim + WHERE sr_item_sk = i_item_sk + AND CAST(d_date as DATE) IN (SELECT d_date + FROM date_dim + WHERE d_week_seq IN + (SELECT d_week_seq + FROM date_dim + WHERE CAST(d_date as DATE) IN (CAST('2000-06-30' AS DATE), CAST('2000-09-27' AS DATE), CAST('2000-11-17' AS DATE)))) + AND sr_returned_date_sk = d_date_sk + GROUP BY i_item_id), + cr_items AS + (SELECT + i_item_id item_id, + sum(cr_return_quantity) cr_item_qty + FROM catalog_returns, item, date_dim + WHERE cr_item_sk = i_item_sk + AND CAST(d_date as DATE) IN (SELECT d_date + FROM date_dim + WHERE d_week_seq IN + (SELECT d_week_seq + FROM date_dim + WHERE CAST(d_date as DATE) IN (CAST('2000-06-30' AS DATE), CAST('2000-09-27' AS DATE), CAST('2000-11-17' AS DATE)))) + AND cr_returned_date_sk = d_date_sk + GROUP BY i_item_id), + wr_items AS + (SELECT + i_item_id item_id, + sum(wr_return_quantity) wr_item_qty + FROM web_returns, item, date_dim + WHERE wr_item_sk = i_item_sk AND CAST(d_date as DATE) IN + (SELECT d_date + FROM date_dim + WHERE d_week_seq IN + (SELECT d_week_seq + FROM date_dim + WHERE CAST(d_date as DATE) IN (CAST('2000-06-30' AS DATE), CAST('2000-09-27' AS DATE), CAST('2000-11-17' AS DATE)))) + AND wr_returned_date_sk = d_date_sk + GROUP BY i_item_id) +SELECT + sr_items.item_id, + sr_item_qty, + sr_item_qty / (sr_item_qty + cr_item_qty + wr_item_qty) / 3.0 * 100 sr_dev, + cr_item_qty, + cr_item_qty / (sr_item_qty + cr_item_qty + wr_item_qty) / 3.0 * 100 cr_dev, + wr_item_qty, + wr_item_qty / (sr_item_qty + cr_item_qty + wr_item_qty) / 3.0 * 100 wr_dev, + (sr_item_qty + cr_item_qty + wr_item_qty) / 3.0 average +FROM sr_items, cr_items, wr_items +WHERE sr_items.item_id = cr_items.item_id + AND sr_items.item_id = wr_items.item_id +ORDER BY sr_items.item_id, sr_item_qty +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q84.sql b/athena-tpcds/src/main/resources/queries/q84.sql new file mode 100644 index 0000000000..a1076b57ce --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q84.sql @@ -0,0 +1,19 @@ +SELECT + c_customer_id AS customer_id, + concat(c_last_name, ', ', c_first_name) AS customername +FROM customer + , customer_address + , customer_demographics + , household_demographics + , income_band + , store_returns +WHERE ca_city = 'Edgewood' + AND c_current_addr_sk = ca_address_sk + AND ib_lower_bound >= 38128 + AND ib_upper_bound <= 38128 + 50000 + AND ib_income_band_sk = hd_income_band_sk + AND cd_demo_sk = c_current_cdemo_sk + AND hd_demo_sk = c_current_hdemo_sk + AND sr_cdemo_sk = cd_demo_sk +ORDER BY c_customer_id +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q85.sql b/athena-tpcds/src/main/resources/queries/q85.sql new file mode 100644 index 0000000000..cf718b0f8a --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q85.sql @@ -0,0 +1,82 @@ +SELECT + substr(r_reason_desc, 1, 20), + avg(ws_quantity), + avg(wr_refunded_cash), + avg(wr_fee) +FROM web_sales, web_returns, web_page, customer_demographics cd1, + customer_demographics cd2, customer_address, date_dim, reason +WHERE ws_web_page_sk = wp_web_page_sk + AND ws_item_sk = wr_item_sk + AND ws_order_number = wr_order_number + AND ws_sold_date_sk = d_date_sk AND d_year = 2000 + AND cd1.cd_demo_sk = wr_refunded_cdemo_sk + AND cd2.cd_demo_sk = wr_returning_cdemo_sk + AND ca_address_sk = wr_refunded_addr_sk + AND r_reason_sk = wr_reason_sk + AND + ( + ( + cd1.cd_marital_status = 'M' + AND + cd1.cd_marital_status = cd2.cd_marital_status + AND + cd1.cd_education_status = 'Advanced Degree' + AND + cd1.cd_education_status = cd2.cd_education_status + AND + ws_sales_price BETWEEN 100.00 AND 150.00 + ) + OR + ( + cd1.cd_marital_status = 'S' + AND + cd1.cd_marital_status = cd2.cd_marital_status + AND + cd1.cd_education_status = 'College' + AND + cd1.cd_education_status = cd2.cd_education_status + AND + ws_sales_price BETWEEN 50.00 AND 100.00 + ) + OR + ( + cd1.cd_marital_status = 'W' + AND + cd1.cd_marital_status = cd2.cd_marital_status + AND + cd1.cd_education_status = '2 yr Degree' + AND + cd1.cd_education_status = cd2.cd_education_status + AND + ws_sales_price BETWEEN 150.00 AND 200.00 + ) + ) + AND + ( + ( + ca_country = 'United States' + AND + ca_state IN ('IN', 'OH', 'NJ') + AND ws_net_profit BETWEEN 100 AND 200 + ) + OR + ( + ca_country = 'United States' + AND + ca_state IN ('WI', 'CT', 'KY') + AND ws_net_profit BETWEEN 150 AND 300 + ) + OR + ( + ca_country = 'United States' + AND + ca_state IN ('LA', 'IA', 'AR') + AND ws_net_profit BETWEEN 50 AND 250 + ) + ) +GROUP BY r_reason_desc +ORDER BY substr(r_reason_desc, 1, 20) + , avg(ws_quantity) + , avg(wr_refunded_cash) + , avg(wr_fee) +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q87.sql b/athena-tpcds/src/main/resources/queries/q87.sql new file mode 100644 index 0000000000..4aaa9f39dc --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q87.sql @@ -0,0 +1,28 @@ +SELECT count(*) +FROM ((SELECT DISTINCT + c_last_name, + c_first_name, + d_date +FROM store_sales, date_dim, customer +WHERE store_sales.ss_sold_date_sk = date_dim.d_date_sk + AND store_sales.ss_customer_sk = customer.c_customer_sk + AND d_month_seq BETWEEN 1200 AND 1200 + 11) + EXCEPT + (SELECT DISTINCT + c_last_name, + c_first_name, + d_date + FROM catalog_sales, date_dim, customer + WHERE catalog_sales.cs_sold_date_sk = date_dim.d_date_sk + AND catalog_sales.cs_bill_customer_sk = customer.c_customer_sk + AND d_month_seq BETWEEN 1200 AND 1200 + 11) + EXCEPT + (SELECT DISTINCT + c_last_name, + c_first_name, + d_date + FROM web_sales, date_dim, customer + WHERE web_sales.ws_sold_date_sk = date_dim.d_date_sk + AND web_sales.ws_bill_customer_sk = customer.c_customer_sk + AND d_month_seq BETWEEN 1200 AND 1200 + 11) + ) cool_cust diff --git a/athena-tpcds/src/main/resources/queries/q88.sql b/athena-tpcds/src/main/resources/queries/q88.sql new file mode 100644 index 0000000000..25bcd90f41 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q88.sql @@ -0,0 +1,122 @@ +SELECT * +FROM + (SELECT count(*) h8_30_to_9 + FROM store_sales, household_demographics, time_dim, store + WHERE ss_sold_time_sk = time_dim.t_time_sk + AND ss_hdemo_sk = household_demographics.hd_demo_sk + AND ss_store_sk = s_store_sk + AND time_dim.t_hour = 8 + AND time_dim.t_minute >= 30 + AND ( + (household_demographics.hd_dep_count = 4 AND household_demographics.hd_vehicle_count <= 4 + 2) + OR + (household_demographics.hd_dep_count = 2 AND household_demographics.hd_vehicle_count <= 2 + 2) + OR + (household_demographics.hd_dep_count = 0 AND + household_demographics.hd_vehicle_count <= 0 + 2)) + AND store.s_store_name = 'ese') s1, + (SELECT count(*) h9_to_9_30 + FROM store_sales, household_demographics, time_dim, store + WHERE ss_sold_time_sk = time_dim.t_time_sk + AND ss_hdemo_sk = household_demographics.hd_demo_sk + AND ss_store_sk = s_store_sk + AND time_dim.t_hour = 9 + AND time_dim.t_minute < 30 + AND ( + (household_demographics.hd_dep_count = 4 AND household_demographics.hd_vehicle_count <= 4 + 2) + OR + (household_demographics.hd_dep_count = 2 AND household_demographics.hd_vehicle_count <= 2 + 2) + OR + (household_demographics.hd_dep_count = 0 AND + household_demographics.hd_vehicle_count <= 0 + 2)) + AND store.s_store_name = 'ese') s2, + (SELECT count(*) h9_30_to_10 + FROM store_sales, household_demographics, time_dim, store + WHERE ss_sold_time_sk = time_dim.t_time_sk + AND ss_hdemo_sk = household_demographics.hd_demo_sk + AND ss_store_sk = s_store_sk + AND time_dim.t_hour = 9 + AND time_dim.t_minute >= 30 + AND ( + (household_demographics.hd_dep_count = 4 AND household_demographics.hd_vehicle_count <= 4 + 2) + OR + (household_demographics.hd_dep_count = 2 AND household_demographics.hd_vehicle_count <= 2 + 2) + OR + (household_demographics.hd_dep_count = 0 AND + household_demographics.hd_vehicle_count <= 0 + 2)) + AND store.s_store_name = 'ese') s3, + (SELECT count(*) h10_to_10_30 + FROM store_sales, household_demographics, time_dim, store + WHERE ss_sold_time_sk = time_dim.t_time_sk + AND ss_hdemo_sk = household_demographics.hd_demo_sk + AND ss_store_sk = s_store_sk + AND time_dim.t_hour = 10 + AND time_dim.t_minute < 30 + AND ( + (household_demographics.hd_dep_count = 4 AND household_demographics.hd_vehicle_count <= 4 + 2) + OR + (household_demographics.hd_dep_count = 2 AND household_demographics.hd_vehicle_count <= 2 + 2) + OR + (household_demographics.hd_dep_count = 0 AND + household_demographics.hd_vehicle_count <= 0 + 2)) + AND store.s_store_name = 'ese') s4, + (SELECT count(*) h10_30_to_11 + FROM store_sales, household_demographics, time_dim, store + WHERE ss_sold_time_sk = time_dim.t_time_sk + AND ss_hdemo_sk = household_demographics.hd_demo_sk + AND ss_store_sk = s_store_sk + AND time_dim.t_hour = 10 + AND time_dim.t_minute >= 30 + AND ( + (household_demographics.hd_dep_count = 4 AND household_demographics.hd_vehicle_count <= 4 + 2) + OR + (household_demographics.hd_dep_count = 2 AND household_demographics.hd_vehicle_count <= 2 + 2) + OR + (household_demographics.hd_dep_count = 0 AND + household_demographics.hd_vehicle_count <= 0 + 2)) + AND store.s_store_name = 'ese') s5, + (SELECT count(*) h11_to_11_30 + FROM store_sales, household_demographics, time_dim, store + WHERE ss_sold_time_sk = time_dim.t_time_sk + AND ss_hdemo_sk = household_demographics.hd_demo_sk + AND ss_store_sk = s_store_sk + AND time_dim.t_hour = 11 + AND time_dim.t_minute < 30 + AND ( + (household_demographics.hd_dep_count = 4 AND household_demographics.hd_vehicle_count <= 4 + 2) + OR + (household_demographics.hd_dep_count = 2 AND household_demographics.hd_vehicle_count <= 2 + 2) + OR + (household_demographics.hd_dep_count = 0 AND + household_demographics.hd_vehicle_count <= 0 + 2)) + AND store.s_store_name = 'ese') s6, + (SELECT count(*) h11_30_to_12 + FROM store_sales, household_demographics, time_dim, store + WHERE ss_sold_time_sk = time_dim.t_time_sk + AND ss_hdemo_sk = household_demographics.hd_demo_sk + AND ss_store_sk = s_store_sk + AND time_dim.t_hour = 11 + AND time_dim.t_minute >= 30 + AND ( + (household_demographics.hd_dep_count = 4 AND household_demographics.hd_vehicle_count <= 4 + 2) + OR + (household_demographics.hd_dep_count = 2 AND household_demographics.hd_vehicle_count <= 2 + 2) + OR + (household_demographics.hd_dep_count = 0 AND + household_demographics.hd_vehicle_count <= 0 + 2)) + AND store.s_store_name = 'ese') s7, + (SELECT count(*) h12_to_12_30 + FROM store_sales, household_demographics, time_dim, store + WHERE ss_sold_time_sk = time_dim.t_time_sk + AND ss_hdemo_sk = household_demographics.hd_demo_sk + AND ss_store_sk = s_store_sk + AND time_dim.t_hour = 12 + AND time_dim.t_minute < 30 + AND ( + (household_demographics.hd_dep_count = 4 AND household_demographics.hd_vehicle_count <= 4 + 2) + OR + (household_demographics.hd_dep_count = 2 AND household_demographics.hd_vehicle_count <= 2 + 2) + OR + (household_demographics.hd_dep_count = 0 AND + household_demographics.hd_vehicle_count <= 0 + 2)) + AND store.s_store_name = 'ese') s8 diff --git a/athena-tpcds/src/main/resources/queries/q89.sql b/athena-tpcds/src/main/resources/queries/q89.sql new file mode 100644 index 0000000000..75408cb032 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q89.sql @@ -0,0 +1,30 @@ +SELECT * +FROM ( + SELECT + i_category, + i_class, + i_brand, + s_store_name, + s_company_name, + d_moy, + sum(ss_sales_price) sum_sales, + avg(sum(ss_sales_price)) + OVER + (PARTITION BY i_category, i_brand, s_store_name, s_company_name) + avg_monthly_sales + FROM item, store_sales, date_dim, store + WHERE ss_item_sk = i_item_sk AND + ss_sold_date_sk = d_date_sk AND + ss_store_sk = s_store_sk AND + d_year IN (1999) AND + ((i_category IN ('Books', 'Electronics', 'Sports') AND + i_class IN ('computers', 'stereo', 'football')) + OR (i_category IN ('Men', 'Jewelry', 'Women') AND + i_class IN ('shirts', 'birdal', 'dresses'))) + GROUP BY i_category, i_class, i_brand, + s_store_name, s_company_name, d_moy) tmp1 +WHERE CASE WHEN (avg_monthly_sales <> 0) + THEN (abs(sum_sales - avg_monthly_sales) / avg_monthly_sales) + ELSE NULL END > 0.1 +ORDER BY sum_sales - avg_monthly_sales, s_store_name +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q9.sql b/athena-tpcds/src/main/resources/queries/q9.sql new file mode 100644 index 0000000000..539407094e --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q9.sql @@ -0,0 +1,48 @@ +SELECT + CASE WHEN (SELECT count(*) + FROM store_sales[ TABLE_SUFFIX ] + WHERE ss_quantity BETWEEN 1 AND 20) > 62316685 + THEN (SELECT avg(ss_ext_discount_amt) + FROM store_sales[ TABLE_SUFFIX ] + WHERE ss_quantity BETWEEN 1 AND 20) + ELSE (SELECT avg(ss_net_paid) + FROM store_sales[ TABLE_SUFFIX ] + WHERE ss_quantity BETWEEN 1 AND 20) END bucket1, + CASE WHEN (SELECT count(*) + FROM store_sales[ TABLE_SUFFIX ] + WHERE ss_quantity BETWEEN 21 AND 40) > 19045798 + THEN (SELECT avg(ss_ext_discount_amt) + FROM store_sales[ TABLE_SUFFIX ] + WHERE ss_quantity BETWEEN 21 AND 40) + ELSE (SELECT avg(ss_net_paid) + FROM store_sales[ TABLE_SUFFIX ] + WHERE ss_quantity BETWEEN 21 AND 40) END bucket2, + CASE WHEN (SELECT count(*) + FROM store_sales[ TABLE_SUFFIX ] + WHERE ss_quantity BETWEEN 41 AND 60) > 365541424 + THEN (SELECT avg(ss_ext_discount_amt) + FROM store_sales[ TABLE_SUFFIX ] + WHERE ss_quantity BETWEEN 41 AND 60) + ELSE (SELECT avg(ss_net_paid) + FROM store_sales[ TABLE_SUFFIX ] + WHERE ss_quantity BETWEEN 41 AND 60) END bucket3, + CASE WHEN (SELECT count(*) + FROM store_sales[ TABLE_SUFFIX ] + WHERE ss_quantity BETWEEN 61 AND 80) > 216357808 + THEN (SELECT avg(ss_ext_discount_amt) + FROM store_sales[ TABLE_SUFFIX ] + WHERE ss_quantity BETWEEN 61 AND 80) + ELSE (SELECT avg(ss_net_paid) + FROM store_sales[ TABLE_SUFFIX ] + WHERE ss_quantity BETWEEN 61 AND 80) END bucket4, + CASE WHEN (SELECT count(*) + FROM store_sales[ TABLE_SUFFIX ] + WHERE ss_quantity BETWEEN 81 AND 100) > 184483884 + THEN (SELECT avg(ss_ext_discount_amt) + FROM store_sales[ TABLE_SUFFIX ] + WHERE ss_quantity BETWEEN 81 AND 100) + ELSE (SELECT avg(ss_net_paid) + FROM store_sales[ TABLE_SUFFIX ] + WHERE ss_quantity BETWEEN 81 AND 100) END bucket5 +FROM reason[ TABLE_SUFFIX ] +WHERE r_reason_sk = 1 diff --git a/athena-tpcds/src/main/resources/queries/q90.sql b/athena-tpcds/src/main/resources/queries/q90.sql new file mode 100644 index 0000000000..85e35bf8bf --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q90.sql @@ -0,0 +1,19 @@ +SELECT cast(amc AS DECIMAL(15, 4)) / cast(pmc AS DECIMAL(15, 4)) am_pm_ratio +FROM (SELECT count(*) amc +FROM web_sales, household_demographics, time_dim, web_page +WHERE ws_sold_time_sk = time_dim.t_time_sk + AND ws_ship_hdemo_sk = household_demographics.hd_demo_sk + AND ws_web_page_sk = web_page.wp_web_page_sk + AND time_dim.t_hour BETWEEN 8 AND 8 + 1 + AND household_demographics.hd_dep_count = 6 + AND web_page.wp_char_count BETWEEN 5000 AND 5200) at, + (SELECT count(*) pmc + FROM web_sales, household_demographics, time_dim, web_page + WHERE ws_sold_time_sk = time_dim.t_time_sk + AND ws_ship_hdemo_sk = household_demographics.hd_demo_sk + AND ws_web_page_sk = web_page.wp_web_page_sk + AND time_dim.t_hour BETWEEN 19 AND 19 + 1 + AND household_demographics.hd_dep_count = 6 + AND web_page.wp_char_count BETWEEN 5000 AND 5200) pt +ORDER BY am_pm_ratio +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q91.sql b/athena-tpcds/src/main/resources/queries/q91.sql new file mode 100644 index 0000000000..9ca7ce00ac --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q91.sql @@ -0,0 +1,23 @@ +SELECT + cc_call_center_id Call_Center, + cc_name Call_Center_Name, + cc_manager Manager, + sum(cr_net_loss) Returns_Loss +FROM + call_center, catalog_returns, date_dim, customer, customer_address, + customer_demographics, household_demographics +WHERE + cr_call_center_sk = cc_call_center_sk + AND cr_returned_date_sk = d_date_sk + AND cr_returning_customer_sk = c_customer_sk + AND cd_demo_sk = c_current_cdemo_sk + AND hd_demo_sk = c_current_hdemo_sk + AND ca_address_sk = c_current_addr_sk + AND d_year = 1998 + AND d_moy = 11 + AND ((cd_marital_status = 'M' AND cd_education_status = 'Unknown') + OR (cd_marital_status = 'W' AND cd_education_status = 'Advanced Degree')) + AND hd_buy_potential LIKE 'Unknown%' + AND ca_gmt_offset = -7 +GROUP BY cc_call_center_id, cc_name, cc_manager, cd_marital_status, cd_education_status +ORDER BY sum(cr_net_loss) DESC diff --git a/athena-tpcds/src/main/resources/queries/q92.sql b/athena-tpcds/src/main/resources/queries/q92.sql new file mode 100644 index 0000000000..65cb68d5f4 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q92.sql @@ -0,0 +1,16 @@ +SELECT sum(ws_ext_discount_amt) AS "Excess Discount Amount " +FROM web_sales, item, date_dim +WHERE i_manufact_id = 350 + AND i_item_sk = ws_item_sk + AND CAST(d_date as DATE) BETWEEN cast('2000-01-27' AS DATE) AND (cast('2000-01-27' AS DATE) + INTERVAL '90' day) + AND d_date_sk = ws_sold_date_sk + AND ws_ext_discount_amt > + ( + SELECT 1.3 * avg(ws_ext_discount_amt) + FROM web_sales, date_dim + WHERE ws_item_sk = i_item_sk + AND CAST(d_date as DATE) BETWEEN cast('2000-01-27' AS DATE) AND (cast('2000-01-27' AS DATE) + INTERVAL '90' day) + AND d_date_sk = ws_sold_date_sk + ) +ORDER BY sum(ws_ext_discount_amt) +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q93.sql b/athena-tpcds/src/main/resources/queries/q93.sql new file mode 100644 index 0000000000..222dc31c1f --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q93.sql @@ -0,0 +1,19 @@ +SELECT + ss_customer_sk, + sum(act_sales) sumsales +FROM (SELECT + ss_item_sk, + ss_ticket_number, + ss_customer_sk, + CASE WHEN sr_return_quantity IS NOT NULL + THEN (ss_quantity - sr_return_quantity) * ss_sales_price + ELSE (ss_quantity * ss_sales_price) END act_sales +FROM store_sales + LEFT OUTER JOIN store_returns + ON (sr_item_sk = ss_item_sk AND sr_ticket_number = ss_ticket_number) + , + reason +WHERE sr_reason_sk = r_reason_sk AND r_reason_desc = 'reason 28') t +GROUP BY ss_customer_sk +ORDER BY sumsales, ss_customer_sk +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q94.sql b/athena-tpcds/src/main/resources/queries/q94.sql new file mode 100644 index 0000000000..86ae5d6be5 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q94.sql @@ -0,0 +1,23 @@ +SELECT + count(DISTINCT ws_order_number) AS "order count ", + sum(ws_ext_ship_cost) AS "total shipping cost ", + sum(ws_net_profit) AS "total net profit " +FROM + web_sales ws1, date_dim, customer_address, web_site +WHERE + CAST(d_date as DATE) BETWEEN CAST('1999-02-01' AS DATE) AND + (CAST('1999-02-01' AS DATE) + INTERVAL '60' day) + AND ws1.ws_ship_date_sk = d_date_sk + AND ws1.ws_ship_addr_sk = ca_address_sk + AND ca_state = 'IL' + AND ws1.ws_web_site_sk = web_site_sk + AND web_company_name = 'pri' + AND EXISTS(SELECT * + FROM web_sales ws2 + WHERE ws1.ws_order_number = ws2.ws_order_number + AND ws1.ws_warehouse_sk <> ws2.ws_warehouse_sk) + AND NOT EXISTS(SELECT * + FROM web_returns wr1 + WHERE ws1.ws_order_number = wr1.wr_order_number) +ORDER BY count(DISTINCT ws_order_number) +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q95.sql b/athena-tpcds/src/main/resources/queries/q95.sql new file mode 100644 index 0000000000..ae6220a8c4 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q95.sql @@ -0,0 +1,29 @@ +WITH ws_wh AS +(SELECT + ws1.ws_order_number, + ws1.ws_warehouse_sk wh1, + ws2.ws_warehouse_sk wh2 + FROM web_sales ws1, web_sales ws2 + WHERE ws1.ws_order_number = ws2.ws_order_number + AND ws1.ws_warehouse_sk <> ws2.ws_warehouse_sk) +SELECT + count(DISTINCT ws_order_number) AS "order count ", + sum(ws_ext_ship_cost) AS "total shipping cost ", + sum(ws_net_profit) AS "total net profit " +FROM + web_sales ws1, date_dim, customer_address, web_site +WHERE + CAST(d_date as DATE) BETWEEN CAST('1999-02-01' AS DATE) AND + (CAST('1999-02-01' AS DATE) + INTERVAL '60' DAY) + AND ws1.ws_ship_date_sk = d_date_sk + AND ws1.ws_ship_addr_sk = ca_address_sk + AND ca_state = 'IL' + AND ws1.ws_web_site_sk = web_site_sk + AND web_company_name = 'pri' + AND ws1.ws_order_number IN (SELECT ws_order_number + FROM ws_wh) + AND ws1.ws_order_number IN (SELECT wr_order_number + FROM web_returns, ws_wh + WHERE wr_order_number = ws_wh.ws_order_number) +ORDER BY count(DISTINCT ws_order_number) +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q96.sql b/athena-tpcds/src/main/resources/queries/q96.sql new file mode 100644 index 0000000000..7ab17e7bc4 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q96.sql @@ -0,0 +1,11 @@ +SELECT count(*) +FROM store_sales, household_demographics, time_dim, store +WHERE ss_sold_time_sk = time_dim.t_time_sk + AND ss_hdemo_sk = household_demographics.hd_demo_sk + AND ss_store_sk = s_store_sk + AND time_dim.t_hour = 20 + AND time_dim.t_minute >= 30 + AND household_demographics.hd_dep_count = 7 + AND store.s_store_name = 'ese' +ORDER BY count(*) +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q97.sql b/athena-tpcds/src/main/resources/queries/q97.sql new file mode 100644 index 0000000000..e7e0b1a052 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q97.sql @@ -0,0 +1,30 @@ +WITH ssci AS ( + SELECT + ss_customer_sk customer_sk, + ss_item_sk item_sk + FROM store_sales, date_dim + WHERE ss_sold_date_sk = d_date_sk + AND d_month_seq BETWEEN 1200 AND 1200 + 11 + GROUP BY ss_customer_sk, ss_item_sk), + csci AS ( + SELECT + cs_bill_customer_sk customer_sk, + cs_item_sk item_sk + FROM catalog_sales, date_dim + WHERE cs_sold_date_sk = d_date_sk + AND d_month_seq BETWEEN 1200 AND 1200 + 11 + GROUP BY cs_bill_customer_sk, cs_item_sk) +SELECT + sum(CASE WHEN ssci.customer_sk IS NOT NULL AND csci.customer_sk IS NULL + THEN 1 + ELSE 0 END) store_only, + sum(CASE WHEN ssci.customer_sk IS NULL AND csci.customer_sk IS NOT NULL + THEN 1 + ELSE 0 END) catalog_only, + sum(CASE WHEN ssci.customer_sk IS NOT NULL AND csci.customer_sk IS NOT NULL + THEN 1 + ELSE 0 END) store_and_catalog +FROM ssci + FULL OUTER JOIN csci ON (ssci.customer_sk = csci.customer_sk + AND ssci.item_sk = csci.item_sk) +LIMIT 100 diff --git a/athena-tpcds/src/main/resources/queries/q98.sql b/athena-tpcds/src/main/resources/queries/q98.sql new file mode 100644 index 0000000000..4412d07d2d --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q98.sql @@ -0,0 +1,21 @@ +SELECT + i_item_desc, + i_category, + i_class, + i_current_price, + sum(ss_ext_sales_price) AS itemrevenue, + sum(ss_ext_sales_price) * 100 / sum(sum(ss_ext_sales_price)) + OVER + (PARTITION BY i_class) AS revenueratio +FROM + store_sales, item, date_dim +WHERE + ss_item_sk = i_item_sk + AND i_category IN ('Sports', 'Books', 'Home') + AND ss_sold_date_sk = d_date_sk + AND CAST(d_date as DATE) BETWEEN cast('1999-02-22' AS DATE) + AND (cast('1999-02-22' AS DATE) + INTERVAL '30' day) +GROUP BY + i_item_id, i_item_desc, i_category, i_class, i_current_price +ORDER BY + i_category, i_class, i_item_id, i_item_desc, revenueratio diff --git a/athena-tpcds/src/main/resources/queries/q99.sql b/athena-tpcds/src/main/resources/queries/q99.sql new file mode 100644 index 0000000000..18aee25c77 --- /dev/null +++ b/athena-tpcds/src/main/resources/queries/q99.sql @@ -0,0 +1,34 @@ +SELECT + substr(w_warehouse_name, 1, 20), + sm_type, + cc_name, + sum(CASE WHEN (cs_ship_date_sk - cs_sold_date_sk <= 30) + THEN 1 + ELSE 0 END) AS "30 days ", + sum(CASE WHEN (cs_ship_date_sk - cs_sold_date_sk > 30) AND + (cs_ship_date_sk - cs_sold_date_sk <= 60) + THEN 1 + ELSE 0 END) AS "31 - 60 days ", + sum(CASE WHEN (cs_ship_date_sk - cs_sold_date_sk > 60) AND + (cs_ship_date_sk - cs_sold_date_sk <= 90) + THEN 1 + ELSE 0 END) AS "61 - 90 days ", + sum(CASE WHEN (cs_ship_date_sk - cs_sold_date_sk > 90) AND + (cs_ship_date_sk - cs_sold_date_sk <= 120) + THEN 1 + ELSE 0 END) AS "91 - 120 days ", + sum(CASE WHEN (cs_ship_date_sk - cs_sold_date_sk > 120) + THEN 1 + ELSE 0 END) AS ">120 days " +FROM + catalog_sales, warehouse, ship_mode, call_center, date_dim +WHERE + d_month_seq BETWEEN 1200 AND 1200 + 11 + AND cs_ship_date_sk = d_date_sk + AND cs_warehouse_sk = w_warehouse_sk + AND cs_ship_mode_sk = sm_ship_mode_sk + AND cs_call_center_sk = cc_call_center_sk +GROUP BY + substr(w_warehouse_name, 1, 20), sm_type, cc_name +ORDER BY substr(w_warehouse_name, 1, 20), sm_type, cc_name +LIMIT 100 diff --git a/athena-tpcds/src/test/java/com/amazonaws/athena/connectors/tpcds/TPCDSMetadataHandlerTest.java b/athena-tpcds/src/test/java/com/amazonaws/athena/connectors/tpcds/TPCDSMetadataHandlerTest.java new file mode 100644 index 0000000000..2fa6389807 --- /dev/null +++ b/athena-tpcds/src/test/java/com/amazonaws/athena/connectors/tpcds/TPCDSMetadataHandlerTest.java @@ -0,0 +1,224 @@ +/*- + * #%L + * athena-tpcds + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.tpcds; + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.amazonaws.athena.connector.lambda.data.BlockUtils; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetSplitsResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableLayoutResponse; +import com.amazonaws.athena.connector.lambda.metadata.GetTableRequest; +import com.amazonaws.athena.connector.lambda.metadata.GetTableResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListSchemasResponse; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesRequest; +import com.amazonaws.athena.connector.lambda.metadata.ListTablesResponse; +import com.amazonaws.athena.connector.lambda.metadata.MetadataRequestType; +import com.amazonaws.athena.connector.lambda.metadata.MetadataResponse; +import com.amazonaws.athena.connector.lambda.security.FederatedIdentity; +import com.amazonaws.athena.connector.lambda.security.LocalKeyFactory; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Schema; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static com.amazonaws.athena.connectors.tpcds.TPCDSMetadataHandler.SPLIT_NUMBER_FIELD; +import static com.amazonaws.athena.connectors.tpcds.TPCDSMetadataHandler.SPLIT_SCALE_FACTOR_FIELD; +import static com.amazonaws.athena.connectors.tpcds.TPCDSMetadataHandler.SPLIT_TOTAL_NUMBER_FIELD; +import static org.junit.Assert.*; + +@RunWith(MockitoJUnitRunner.class) +public class TPCDSMetadataHandlerTest +{ + private static final Logger logger = LoggerFactory.getLogger(TPCDSMetadataHandlerTest.class); + + private FederatedIdentity identity = new FederatedIdentity("id", "principal", "account"); + private TPCDSMetadataHandler handler; + private BlockAllocator allocator; + + @Mock + private AWSSecretsManager mockSecretsManager; + + @Mock + private AmazonAthena mockAthena; + + @Before + public void setUp() + throws Exception + { + handler = new TPCDSMetadataHandler(new LocalKeyFactory(), mockSecretsManager, mockAthena, "spillBucket", "spillPrefix"); + allocator = new BlockAllocatorImpl(); + } + + @After + public void tearDown() + throws Exception + { + allocator.close(); + } + + @Test + public void doListSchemaNames() + { + logger.info("doListSchemas - enter"); + + ListSchemasRequest req = new ListSchemasRequest(identity, "queryId", "default"); + ListSchemasResponse res = handler.doListSchemaNames(allocator, req); + logger.info("doListSchemas - {}", res.getSchemas()); + + assertTrue(res.getSchemas().size() == 5); + logger.info("doListSchemas - exit"); + } + + @Test + public void doListTables() + { + logger.info("doListTables - enter"); + + ListTablesRequest req = new ListTablesRequest(identity, "queryId", "default", "tpcds1"); + ListTablesResponse res = handler.doListTables(allocator, req); + logger.info("doListTables - {}", res.getTables()); + + assertTrue(res.getTables().contains(new TableName("tpcds1", "customer"))); + + assertTrue(res.getTables().size() == 25); + + logger.info("doListTables - exit"); + } + + @Test + public void doGetTable() + { + logger.info("doGetTable - enter"); + String expectedSchema = "tpcds1"; + + GetTableRequest req = new GetTableRequest(identity, + "queryId", + "default", + new TableName(expectedSchema, "customer")); + + GetTableResponse res = handler.doGetTable(allocator, req); + logger.info("doGetTable - {} {}", res.getTableName(), res.getSchema()); + + assertEquals(new TableName(expectedSchema, "customer"), res.getTableName()); + assertTrue(res.getSchema() != null); + + logger.info("doGetTable - exit"); + } + + @Test + public void doGetTableLayout() + throws Exception + { + logger.info("doGetTableLayout - enter"); + + Map constraintsMap = new HashMap<>(); + + Schema schema = SchemaBuilder.newBuilder().build(); + + GetTableLayoutRequest req = new GetTableLayoutRequest(identity, + "queryId", + "default", + new TableName("tpcds1", "customer"), + new Constraints(constraintsMap), + schema, + Collections.EMPTY_SET); + + GetTableLayoutResponse res = handler.doGetTableLayout(allocator, req); + + logger.info("doGetTableLayout - {}", res.getPartitions().getSchema()); + logger.info("doGetTableLayout - {}", res.getPartitions()); + + assertTrue(res.getPartitions().getRowCount() == 1); + + logger.info("doGetTableLayout - exit"); + } + + @Test + public void doGetSplits() + { + logger.info("doGetSplits: enter"); + + Schema schema = SchemaBuilder.newBuilder() + .addIntField("partitionId") + .build(); + + Block partitions = BlockUtils.newBlock(allocator, "partitionId", Types.MinorType.INT.getType(), 1); + + String continuationToken = null; + GetSplitsRequest originalReq = new GetSplitsRequest(identity, + "queryId", + "catalog_name", + new TableName("tpcds1", "customer"), + partitions, + Collections.EMPTY_LIST, + new Constraints(new HashMap<>()), + continuationToken); + + int numContinuations = 0; + do { + GetSplitsRequest req = new GetSplitsRequest(originalReq, continuationToken); + logger.info("doGetSplits: req[{}]", req); + + MetadataResponse rawResponse = handler.doGetSplits(allocator, req); + assertEquals(MetadataRequestType.GET_SPLITS, rawResponse.getRequestType()); + + GetSplitsResponse response = (GetSplitsResponse) rawResponse; + continuationToken = response.getContinuationToken(); + + logger.info("doGetSplits: continuationToken[{}] - numSplits[{}]", continuationToken, response.getSplits().size()); + + for (Split nextSplit : response.getSplits()) { + assertNotNull(nextSplit.getProperty(SPLIT_NUMBER_FIELD)); + assertNotNull(nextSplit.getProperty(SPLIT_TOTAL_NUMBER_FIELD)); + assertNotNull(nextSplit.getProperty(SPLIT_SCALE_FACTOR_FIELD)); + } + + if (continuationToken != null) { + numContinuations++; + } + } + while (continuationToken != null); + + assertTrue(numContinuations == 0); + + logger.info("doGetSplits: exit"); + } +} diff --git a/athena-tpcds/src/test/java/com/amazonaws/athena/connectors/tpcds/TPCDSRecordHandlerTest.java b/athena-tpcds/src/test/java/com/amazonaws/athena/connectors/tpcds/TPCDSRecordHandlerTest.java new file mode 100644 index 0000000000..01baaafa64 --- /dev/null +++ b/athena-tpcds/src/test/java/com/amazonaws/athena/connectors/tpcds/TPCDSRecordHandlerTest.java @@ -0,0 +1,277 @@ +/*- + * #%L + * athena-tpcds + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.tpcds; + +import com.amazonaws.athena.connector.lambda.data.Block; +import com.amazonaws.athena.connector.lambda.data.BlockAllocator; +import com.amazonaws.athena.connector.lambda.data.BlockAllocatorImpl; +import com.amazonaws.athena.connector.lambda.data.BlockUtils; +import com.amazonaws.athena.connector.lambda.data.S3BlockSpillReader; +import com.amazonaws.athena.connector.lambda.data.SchemaBuilder; +import com.amazonaws.athena.connector.lambda.domain.Split; +import com.amazonaws.athena.connector.lambda.domain.TableName; +import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints; +import com.amazonaws.athena.connector.lambda.domain.predicate.EquatableValueSet; +import com.amazonaws.athena.connector.lambda.domain.predicate.Range; +import com.amazonaws.athena.connector.lambda.domain.predicate.SortedRangeSet; +import com.amazonaws.athena.connector.lambda.domain.predicate.ValueSet; +import com.amazonaws.athena.connector.lambda.domain.spill.S3SpillLocation; +import com.amazonaws.athena.connector.lambda.domain.spill.SpillLocation; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsRequest; +import com.amazonaws.athena.connector.lambda.records.ReadRecordsResponse; +import com.amazonaws.athena.connector.lambda.records.RecordResponse; +import com.amazonaws.athena.connector.lambda.records.RemoteReadRecordsResponse; +import com.amazonaws.athena.connector.lambda.security.EncryptionKeyFactory; +import com.amazonaws.athena.connector.lambda.security.FederatedIdentity; +import com.amazonaws.athena.connector.lambda.security.LocalKeyFactory; +import com.amazonaws.services.athena.AmazonAthena; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.PutObjectResult; +import com.amazonaws.services.s3.model.S3Object; +import com.amazonaws.services.s3.model.S3ObjectInputStream; +import com.amazonaws.services.secretsmanager.AWSSecretsManager; +import com.google.common.io.ByteStreams; +import com.teradata.tpcds.Table; +import com.teradata.tpcds.column.Column; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Schema; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.runners.MockitoJUnitRunner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import static com.amazonaws.athena.connectors.tpcds.TPCDSMetadataHandler.SPLIT_NUMBER_FIELD; +import static com.amazonaws.athena.connectors.tpcds.TPCDSMetadataHandler.SPLIT_SCALE_FACTOR_FIELD; +import static com.amazonaws.athena.connectors.tpcds.TPCDSMetadataHandler.SPLIT_TOTAL_NUMBER_FIELD; +import static org.junit.Assert.*; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class TPCDSRecordHandlerTest +{ + private static final Logger logger = LoggerFactory.getLogger(TPCDSRecordHandlerTest.class); + + private FederatedIdentity identity = new FederatedIdentity("id", "principal", "account"); + private List mockS3Storage; + private TPCDSRecordHandler handler; + private S3BlockSpillReader spillReader; + private BlockAllocator allocator; + private EncryptionKeyFactory keyFactory = new LocalKeyFactory(); + private Table table; + private Schema schemaForRead; + + @Mock + private AmazonS3 mockS3; + + @Mock + private AWSSecretsManager mockSecretsManager; + + @Mock + private AmazonAthena mockAthena; + + @Before + public void setUp() + throws Exception + { + for (Table next : Table.getBaseTables()) { + if (next.getName().equals("customer")) { + table = next; + } + } + + SchemaBuilder schemaBuilder = SchemaBuilder.newBuilder(); + for (Column nextCol : table.getColumns()) { + schemaBuilder.addField(TPCDSUtils.convertColumn(nextCol)); + } + schemaForRead = schemaBuilder.build(); + + mockS3Storage = new ArrayList<>(); + allocator = new BlockAllocatorImpl(); + handler = new TPCDSRecordHandler(mockS3, mockSecretsManager, mockAthena); + spillReader = new S3BlockSpillReader(mockS3, allocator); + + when(mockS3.putObject(anyObject(), anyObject(), anyObject(), anyObject())) + .thenAnswer((InvocationOnMock invocationOnMock) -> + { + synchronized (mockS3Storage) { + InputStream inputStream = (InputStream) invocationOnMock.getArguments()[2]; + ByteHolder byteHolder = new ByteHolder(); + byteHolder.setBytes(ByteStreams.toByteArray(inputStream)); + mockS3Storage.add(byteHolder); + return mock(PutObjectResult.class); + } + }); + + when(mockS3.getObject(anyString(), anyString())) + .thenAnswer((InvocationOnMock invocationOnMock) -> + { + synchronized (mockS3Storage) { + S3Object mockObject = mock(S3Object.class); + ByteHolder byteHolder = mockS3Storage.get(0); + mockS3Storage.remove(0); + when(mockObject.getObjectContent()).thenReturn( + new S3ObjectInputStream( + new ByteArrayInputStream(byteHolder.getBytes()), null)); + return mockObject; + } + }); + } + + @After + public void tearDown() + throws Exception + { + allocator.close(); + } + + @Test + public void doReadRecordsNoSpill() + throws Exception + { + logger.info("doReadRecordsNoSpill: enter"); + + Map constraintsMap = new HashMap<>(); + constraintsMap.put("c_customer_id", EquatableValueSet.newBuilder(allocator, Types.MinorType.VARCHAR.getType(), true, false) + .add("AAAAAAAABAAAAAAA") + .add("AAAAAAAACAAAAAAA") + .add("AAAAAAAADAAAAAAA").build()); + + ReadRecordsRequest request = new ReadRecordsRequest(identity, + "catalog", + "queryId-" + System.currentTimeMillis(), + new TableName("tpcds1", table.getName()), + schemaForRead, + Split.newBuilder(S3SpillLocation.newBuilder() + .withBucket(UUID.randomUUID().toString()) + .withSplitId(UUID.randomUUID().toString()) + .withQueryId(UUID.randomUUID().toString()) + .withIsDirectory(true) + .build(), + keyFactory.create()) + .add(SPLIT_NUMBER_FIELD, "0") + .add(SPLIT_TOTAL_NUMBER_FIELD, "1000") + .add(SPLIT_SCALE_FACTOR_FIELD, "1") + .build(), + new Constraints(constraintsMap), + 100_000_000_000L, + 100_000_000_000L //100GB don't expect this to spill + ); + + RecordResponse rawResponse = handler.doReadRecords(allocator, request); + + assertTrue(rawResponse instanceof ReadRecordsResponse); + + ReadRecordsResponse response = (ReadRecordsResponse) rawResponse; + logger.info("doReadRecordsNoSpill: rows[{}]", response.getRecordCount()); + + assertTrue(response.getRecords().getRowCount() == 3); + logger.info("doReadRecordsNoSpill: {}", BlockUtils.rowToString(response.getRecords(), 0)); + + logger.info("doReadRecordsNoSpill: exit"); + } + + @Test + public void doReadRecordsSpill() + throws Exception + { + logger.info("doReadRecordsSpill: enter"); + + Map constraintsMap = new HashMap<>(); + constraintsMap.put("c_current_cdemo_sk", SortedRangeSet.of( + Range.range(allocator, Types.MinorType.BIGINT.getType(), 100L, true, 100_000_000L, true))); + + ReadRecordsRequest request = new ReadRecordsRequest(identity, + "catalog", + "queryId-" + System.currentTimeMillis(), + new TableName("tpcds1", table.getName()), + schemaForRead, + Split.newBuilder(S3SpillLocation.newBuilder() + .withBucket(UUID.randomUUID().toString()) + .withSplitId(UUID.randomUUID().toString()) + .withQueryId(UUID.randomUUID().toString()) + .withIsDirectory(true) + .build(), + keyFactory.create()) + .add(SPLIT_NUMBER_FIELD, "0") + .add(SPLIT_TOTAL_NUMBER_FIELD, "10000") + .add(SPLIT_SCALE_FACTOR_FIELD, "1") + .build(), + new Constraints(constraintsMap), + 1_500_000L, //~1.5MB so we should see some spill + 0 + ); + + RecordResponse rawResponse = handler.doReadRecords(allocator, request); + + assertTrue(rawResponse instanceof RemoteReadRecordsResponse); + + try (RemoteReadRecordsResponse response = (RemoteReadRecordsResponse) rawResponse) { + logger.info("doReadRecordsSpill: remoteBlocks[{}]", response.getRemoteBlocks().size()); + + assertTrue(response.getNumberBlocks() > 1); + + int blockNum = 0; + for (SpillLocation next : response.getRemoteBlocks()) { + S3SpillLocation spillLocation = (S3SpillLocation) next; + try (Block block = spillReader.read(spillLocation, response.getEncryptionKey(), response.getSchema())) { + + logger.info("doReadRecordsSpill: blockNum[{}] and recordCount[{}]", blockNum++, block.getRowCount()); + // assertTrue(++blockNum < response.getRemoteBlocks().size() && block.getRowCount() > 10_000); + + logger.info("doReadRecordsSpill: {}", BlockUtils.rowToString(block, 0)); + assertNotNull(BlockUtils.rowToString(block, 0)); + } + } + } + + logger.info("doReadRecordsSpill: exit"); + } + + private class ByteHolder + { + private byte[] bytes; + + public void setBytes(byte[] bytes) + { + this.bytes = bytes; + } + + public byte[] getBytes() + { + return bytes; + } + } +} diff --git a/athena-udfs/LICENSE.txt b/athena-udfs/LICENSE.txt new file mode 100644 index 0000000000..67db858821 --- /dev/null +++ b/athena-udfs/LICENSE.txt @@ -0,0 +1,175 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. diff --git a/athena-udfs/README.md b/athena-udfs/README.md new file mode 100644 index 0000000000..a2dde7c694 --- /dev/null +++ b/athena-udfs/README.md @@ -0,0 +1,22 @@ +# Amazon Athena UDF Connector + +This connector extends Amazon Athena's capability by adding customizable UDFs via Lambda. + +## Supported UDFs + +TODO: Add supported UDFs + +### Deploying The Connector + +To use this connector in your queries, navigate to AWS Serverless Application Repository and deploy a pre-built version of this connector. Alternatively, you can build and deploy this connector from source follow the below steps or use the more detailed tutorial in the athena-example module: + +1. From the athena-federation-sdk dir, run `mvn clean install` if you haven't already. +2. From the athena-udfs dir, run `mvn clean install`. +3. From the athena-udfs dir, run `../tools/publish.sh S3_BUCKET_NAME athena-udfs` to publish the connector to your private AWS Serverless Application Repository. The S3_BUCKET in the command is where a copy of the connector's code will be stored for Serverless Application Repository to retrieve it. This will allow users with permission to do so, the ability to deploy instances of the connector via 1-Click form. Then navigate to [Serverless Application Repository](https://aws.amazon.com/serverless/serverlessrepo) +4. Try using your UDF(s) in a query. + + + +## License + +This project is licensed under the Apache-2.0 License. \ No newline at end of file diff --git a/athena-udfs/athena-udfs.yaml b/athena-udfs/athena-udfs.yaml new file mode 100644 index 0000000000..6c9a135a49 --- /dev/null +++ b/athena-udfs/athena-udfs.yaml @@ -0,0 +1,40 @@ +Transform: 'AWS::Serverless-2016-10-31' +Metadata: + 'AWS::ServerlessRepo::Application': + Name: AthenaUserDefinedFunctions + Description: 'This connector enables Amazon Athena to leverage common UDFs made available via Lambda.' + Author: 'Amazon Athena' + SpdxLicenseId: Apache-2.0 + LicenseUrl: LICENSE.txt + ReadmeUrl: README.md + Labels: + - athena-federation + HomePageUrl: 'https://github.com/awslabs/aws-athena-query-federation' + SemanticVersion: 1.0.0 + SourceCodeUrl: 'https://github.com/awslabs/aws-athena-query-federation' +Parameters: + Parameters: + UDFFunctionName: + Description: 'The name you want to give the Lambda function that will contain your UDFs.' + Type: String + LambdaTimeout: + Description: 'Maximum Lambda invocation runtime in seconds. (min 1 - 900 max)' + Default: 900 + Type: Number + LambdaMemory: + Description: 'Lambda memory in MB (min 128 - 3008 max).' + Default: 3008 + Type: Number +Resources: + ConnectorConfig: + Type: 'AWS::Serverless::Function' + Properties: + Environment: + Variables: + FunctionName: !Ref UDFFunctionName + Handler: "com.amazonaws.athena.connectors.udfs.AthenaUDFHandler" + CodeUri: "./target/athena-udfs-1.0.jar" + Description: "This connector enables Amazon Athena to leverage common UDFs made available via Lambda." + Runtime: java8 + Timeout: !Ref LambdaTimeout + MemorySize: !Ref LambdaMemory \ No newline at end of file diff --git a/athena-udfs/pom.xml b/athena-udfs/pom.xml new file mode 100644 index 0000000000..b9cdfe163c --- /dev/null +++ b/athena-udfs/pom.xml @@ -0,0 +1,57 @@ + + + + aws-athena-query-federation + com.amazonaws + 1.0 + + 4.0.0 + + athena-udfs + + + + com.amazonaws + aws-athena-federation-sdk + ${aws-athena-federation-sdk.version} + + + com.google.guava + guava + 21.0 + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.1 + + false + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + package + + shade + + + + + + + \ No newline at end of file diff --git a/athena-udfs/src/main/java/com/amazonaws/athena/connectors/udfs/AthenaUDFHandler.java b/athena-udfs/src/main/java/com/amazonaws/athena/connectors/udfs/AthenaUDFHandler.java new file mode 100644 index 0000000000..ee6211438a --- /dev/null +++ b/athena-udfs/src/main/java/com/amazonaws/athena/connectors/udfs/AthenaUDFHandler.java @@ -0,0 +1,117 @@ +/*- + * #%L + * athena-udfs + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.udfs; + +import com.amazonaws.athena.connector.lambda.udf.UserDefinedFunctionHandler; +import com.google.common.collect.ImmutableMap; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class AthenaUDFHandler + extends UserDefinedFunctionHandler +{ + public Boolean example_udf(Boolean value) + { + return !value; + } + + public Byte example_udf(Byte value) + { + return (byte) (value + 1); + } + + public Short example_udf(Short value) + { + return (short) (value + 1); + } + + public Integer example_udf(Integer value) + { + return value + 1; + } + + public Long example_udf(Long value) + { + return value + 1; + } + + public Float example_udf(Float value) + { + return value + 1; + } + + public Double example_udf(Double value) + { + return value + 1; + } + + public BigDecimal example_udf(BigDecimal value) + { + BigDecimal one = new BigDecimal(1); + one.setScale(value.scale(), RoundingMode.HALF_UP); + return value.add(one); + } + + public String example_udf(String value) + { + return value + "_dada"; + } + + public LocalDateTime example_udf(LocalDateTime value) + { + return value.minusDays(1); + } + + public LocalDate example_udf(LocalDate value) + { + return value.minusDays(1); + } + + public List example_udf(List value) + { + System.out.println("Array input: " + value); + List result = value.stream().map(o -> ((Integer) o) + 1).collect(Collectors.toList()); + System.out.println("Array output: " + result); + return result; + } + + public Map example_udf(Map value) + { + Long longVal = (Long) value.get("x"); + Double doubleVal = (Double) value.get("y"); + + return ImmutableMap.of("x", longVal + 1, "y", doubleVal + 1.0); + } + + public byte[] example_udf(byte[] value) + { + byte[] output = new byte[value.length]; + for (int i = 0; i < value.length; ++i) { + output[i] = (byte) (value[i] + 1); + } + return output; + } +} diff --git a/athena-udfs/src/test/java/com/amazonaws/athena/connectors/udfs/AthenaUDFHandlerTest.java b/athena-udfs/src/test/java/com/amazonaws/athena/connectors/udfs/AthenaUDFHandlerTest.java new file mode 100644 index 0000000000..8306c08c95 --- /dev/null +++ b/athena-udfs/src/test/java/com/amazonaws/athena/connectors/udfs/AthenaUDFHandlerTest.java @@ -0,0 +1,27 @@ +/*- + * #%L + * athena-udfs + * %% + * Copyright (C) 2019 Amazon Web Services + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.amazonaws.athena.connectors.udfs; + +import static org.junit.Assert.*; + +public class AthenaUDFHandlerTest +{ + +} diff --git a/checkstyle.xml b/checkstyle.xml new file mode 100644 index 0000000000..ccd3c1f750 --- /dev/null +++ b/checkstyle.xml @@ -0,0 +1,171 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/img/athena_federation_demo.png b/docs/img/athena_federation_demo.png new file mode 100644 index 0000000000000000000000000000000000000000..4c4666201f61911038d59d14e3010df7352c1625 GIT binary patch literal 147728 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%0DBX$q%>%Er@Fz86(^Eo;j#^ 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 literal 0 HcmV?d00001 diff --git a/docs/img/athena_federation_flow.png b/docs/img/athena_federation_flow.png new file mode 100644 index 0000000000000000000000000000000000000000..6b5dbdc5ce81c5eac1e0bae97b8f9dbfd3f78e51 GIT binary patch literal 33218 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 }C{R#P=&~{rs!&iczaT$#NHCDUn9AohK|uvW z$x4W-d#|1W5%txzR{xlp#?!CRlB5Pq*5rD4O%8RQUL3q!I(|_xdsjawQG~!lp93na zx1-iPW0WlRQpQfEhncN}QH=z^qV-R_eS7=e?@8DT?A)ecW)0Z7c@m%y;y%pxyXdmQ zaO=JiC=*zS6Dv?=#6#f`y~ERlMu~X+4UP=&fByjq0C~#m*Z(&ku-sqY4f)pp<^$Cc zNSSf|gR-4nWkkf_WG-(#Gvq0u7$_w(%p4rN@7}$8|Ni3Sq>otnChg0YFJ<|@C+97j zLqEg*y(VGt;pXsj%E;6drX7?hVuaZ1^KhjF1qFkC-+Y$k?Xg{8u7Z9*>+rh#-tNHy zd2WQ*v}nQ5XE8A`Q2*~A{g~UAd!woHa&mPD<~_OjzP69I$A5)}Jd8Lv& zF1zl(f6=G}iAB2HglW?M6HpmN-O%v&S>x32m&>52B_$=LX}76-!Q4?6cdx)hFmN=L z;m?IvkKe<7CKbGMS$+N0Zjx~RI~yFbdt_A9RKBYO-tXGY_AA%>q!D zVnwe<$uzw`o>?&7>3hfA>FDT4FX&=!UaUZf`od1?>gINOa$;+1OKpC0kn05FkB5gR z^4`wQPG+Uqfh2%B?cnrO{-bGZQc@IJ&!3me^bJ`(JrZNwy_GIMUZA$BDokd+Pyirz zlAZ}29UYaBJ=~t0oSe%G2?Uj-rlvMGI}7s;Dh2@ofu=UZFuj0fVF_N_=l8?m;Y7BY zuCAP8;P2;$;K=OkZ1MJog8$aJy2xI2%lAgsA*Nmc^!vCZE7n$jDHV`XslH8Z5@Ma62){1(0GO_A`;k zN`Q(JLZ0P?#Kp;3aFdmjGxkd<(g600sPvr&Cg}$#C&;PFTfODt3QgM`PD=W5prQ32 z6X;4#PM+3rT>ToOBr7W$f_L=G&ek?duzupxnxNlLk;1q*R3OX))jmhiL)^2*$?0jE zzN5SQtzB&wf{*ok)05ldIp-saKfk2K#bLPvW(p-gkz-?1r3S5jwfQLg@{TEXefw;s zF`KBfvy;*VVu&=g&ozMk_oEtA72L$z z+xT0PutY$5%)hXv-g7XKeFJe{t|uj3y=MCY{#BDO@Hj^gT0o-+C8ZM!GjlXaWVfk< z16y`mHDnuS^y1>;RQd@L<1!#r+h_?S;Czop;i3g0?8Gvl(>>=1=PMyyKp z{{*p@)lQ$riN`=L>TUunt3#78jNUPbJ3DcGw^*#w5fv4+fA4oPUvcYh_gLQ9*WZ8a z+TPwy`Nt{nhQ!r+ERE@Osg|M0O$r8ZvINx$9v^=?6#gUYXxtx(F5XpBR(6={)Ge#T zaLO`^d$7Q2{@=uZ-N;I z{Xt$p6mc%{4Xb zjx&EzOu0yk@8)1qug#SXVt_w?!ZXg7mqWm%f4j<{<0r)NBo{?rH&d?iDLdF76 z0=9S}h%C?%Y!GoF->>6`{6PHT6)!y*81b4F{s!*83%H2BjF`WASO$XrvjU|9_dhGd zOuRz>#|q_TE|tG|3SkA9VXrI1OvWYt<`QR0|Ggq&1NlEIUSIz|RxsK_aN`x< zAuIlWHu$>Yb%XyUg7W{^;QzM>|NZ*^dxQTGf${%+gC<^uSAc82!x6&l_B4wSG6R$h zbWeMtH-u*&maj?caMP}Om1>~I;rW|z;xS8GR6+N27G*tnIQ)&wNy{s>5(D6X^q>p7 zF>sK9$XTB)XMJ2HDo0I$LXaYMz5FH~ZbxTxc-FU7)i94Vq8gSW`+)43N7opQH$n1M z*C<&8+Udyk=nGmUnr7|E+SNqM^mPC?)6Io29py#i`Zj1idRu`n-CaxK%YIt(Ypbt1 zhiEhzk&OP7RU^zpv-}&Na^4uYFs5^uJKPU|9MP@{G<+ABuM1=Sdz8iB9H}EUKb9+ya`)uWd)TbNx@TVKvL zn8{aN%}`$dDY8ziwoITk@2Ry655m=%zN#x1R5GoPaEKW^x3M_q(;$;X2QIK}7;GQ` zaJ6jUksp8eBx2dk*PG=7I6IY}wIrsAK7;y&sHXrQ&HuilXntlvO?xw0Wzkc(hfiUd zGa;5GQ&kY9QFv%X_tx z0c9v|>}dA3he=`@FMo-Y>7J)+7f1OkC zi84@4UsOKO0g)0f6#QYXu1F=A28^ljZ59P5jc62#FO%fY=8rtVgzCFrwQ-=L3Q+1* z9G?5zNQhndPS~EFZKpLox}08GEd)C?cwgQ79OPp#x4vP|B=QQ8;)m>T@m>igCZL#A}!-ue9Xh*wQ%Qc+&xk z;(L*KTdH+OzBiYV(b#G>s@j(Nx7v z@$jok$m?|her-Hm-y*t%3cr)AJ3gI}LuCezU%Xdz;8C&$c-OjD>V0Sb_{}kC9q1?! zhiKs&^04hQ`3-(vX77n^<2c>G*e2be0w0Je-yVwE-WjpM-WgI-@s*@a8ADQFV>i{C z-na;HtD{>OgaHPo*Nq1@2KD z_$y-p)DGN9DBvx0)s6W-r9kgMTn50|+b7VZ$kE_?4-BPnyyl2ZC%q+@;FZbo;&VxM zxmLZoZAx6PcJz6Akq1jGt(tqzSn;WdyR~op68u5kRn>1af1Wmn$=l)zfVlx&5H>@O zHaQuf^1wz;)D-e8H9RoZyDs%755dT>A6NzWs08$rsYXUX^US?hTtJf z<-6g5*wZRz?~WC|Q~t7v2L##Axpj#?7Ms!7ZGsb4Ia(9%Cm{fRH`yYDs# zZVCAfJu}|iCm2%g#}D5nSxDK+_k%D(p z=7h(V#i!3XP728+g?u(v@Evw?2#kTxCkOt zyED+Uvv-qgX9F{+6Nt0E%JbV3r{d)T%sY`F?&cihxlBl48LBlx;ei%CZ2T%T?K5kp z4e<~&swAN?WR4TI>dR?N_75H(&9Mxy>(5XX^gWua)Otm@ciKnzoZzJHA^IeENzB584IlxMu-xet z<}k;8NG*Q5*sDJU!MUeBdgRx=A!t^ZFF$qcPQy2q^G!u1Mx#avavfdl9^E(`-Q3T( z>A$O*sx9?5OTB87ZO~D$`B3}5t1i(%{d1lX?gYDAAZ3cKLbsV5kftRH?E;zO2uqy@ zf&*$KJ_KHPi_Lfo&7oJ7dg6mXr2w2s#QuHv+~qL9a$>f@ z>W{NvE;2xbFD2Wk3d<3lr~!6QeGOtcKSA8zej{SJ*YVGwP0I?-4+G2y`)wVFT|f0y zw>@G1jc-ya=Nny#DcU%}V&0y2s&i?VK<*~e548Gs{Y_QeArB;^%a0}zn)MbLDi1$r zeC}uEQzdw8b^saWLu`ijxJ+N=zqow*;F{;^TZp>qI!3H^ZO0u)(jZ41dc*W+lGzmd zCAUO#fpok!w9&pOyxFDXr_SuCPCg$SG?9h#$b;zbyDa~Xa)FUuoLnaorcR)`Jr=v6 zWk&n2vEUWMcpIatr%(WwAtE`PsMSP>@%CT~gbrH<#us9+$_chIdN}uhoz1kF*~vUA zhaR*!mz&J!jAtCb2nZ(8{|z~@C|L4jR5PIn2AiEgM?Ml#z8_v)WPV|=h;K|26o;ss z3e;3Bj6IwDObh%}sh$%?YGm-222t(u%mzG<*=98yT={3^b6PDDR!S0hDPpT>B0eRY z>47V)M=Mi^^Q>{Rt?;SPcGA8XRmvnAxQ4kebCqp822!Puyrs;C^2`>c42p>{9i#d= zxR91rh@pz{QEdtHvvpYV_|UdL3lVhvwzv72(mPmpRSX$Xt28t1Yxca^&i8kRCh0M& z7D~01N2I5J9ml$&q;W}f2aL>g^!u4P0vk16TftY`mB*)t@Qnw+(r$c9*MvNg`1+C6=%C^Tn}75JU`<>8Nl18kB{QsntE91m=d3P*9#-FpcP zeEsr-#t}{>QSJ!_FX7LW$SIDBZ}~i=*dpoN@W(eE`g5Z^Uc4)+?5tS-#;T%{I_ek- zx-zdDo>-~_68ceASnqWsb5afaz#iIQgNtN!H20l!ebW9~CA@`5E>eIx*wEUI4~guw^uG@dnb zkkyBE5yw13nBXtCsi@e*xi>7x*?~<{_u!?fKBA=v9q0DER0|H@idIM}tTYW=%NVBH zi6x5~U!)s%%SKOARVU{i0oU@im4}(jKVtI+m*BrIR%xBf5_K*sW@`M3>^+jcwtZdq zmZY_;lSxvaB2UAFm^uDHV{@w7Gm-5r+Mf8RvDCL&LaocpRV%fp;8hnuLO-euwFAGw zhcBXsB+~3WCydg#OLF)izE%#l>UahU-(qd#NVZ{dT41Ihs-`R&Hh2zA%a@Z3uv0W#c1;RY zu>R}_=F`aooIeC_Ucaqn7gf*9GRjOd!fZ`tt@+4gFH_=N#@Qtk%a1XC06f=BE<_9^ zRVu91Fhd5= zO5P1^ac#4KK1ZMVnYlalY9K&M3-e}G9=wQHy6z-luwX-ivCrA+JyTpASjJSlTJfdl zl6+D(^yN=*;ZK!#6G#c35Zg&7Pep;kYDOJ8@fL`0Ihx36`uS_aO<*VL@3xFlRii{j ze~l{8dnIj>Of3?QLT5y6h6+$VU52+8Z7b{$bMdv28SmQ0R1X0|BL1RkuLS&;aKdLF z&R~@#{pZax>6>$i>+Sj4y#Xf5#HFW3uM3Jud zBBG2-Q=R}hgv9HMy(PMXB?i^+K!iGTmws_hkJyinl^Vq(blq562e%ufcjx}W)AU6^ zg~4__Ly5A}2WMd(VkmQ64EuvMMRxzuL|tOu8{2hW-+(_x8r;xZBervxQNt8w4r6(; ze^DLfh_zVM>H{ZhJ0s5$jsiM`ITePZSSo;%wF8zetPza$FJ4hUvD%VOWNO+IP3zdF zbgsXPm}m!_qz7DM5KbuI)WeK%CNXuTYoFm5?fn2z=IP;2*7{JEpZ5cGd&$PW(R>3I zhd#FYj}`55Sl`I73-0&RNurP5`n$2I=~c*E-RYyT70)%<&y6@2K19lnPpWo*T7T;> z&c;<})cZI-(s2?Fc_9BrXS{%F8bQ2sT zXhr%uRZni54A)6nkiX_xBN`j521&o%xCe2Y35n}5ynn=pMhthgY2lvATE6@+je2t> zs05$oWt%PB!NuG{pJU`_q= zniTsIX(zCu0|dUbnUT(w;%y5id<)o}IWmTdcs&_?WP#_fCJyo8-ljJvA>MuAct_q= zya*Or!0XXA@1&riq_Wd&2cmRLb6Ae(mG2xslvv!%(#h|3`t)kkxV$S5$GOo643Q_p z(#*aKoZh~-wa4nXDdwP14Ro2xsoevYq)s$e^sevMiNOKuVSo^-!^|Dow+>@&$QnZn zjRS926OvMr9-(nYE?@%2mT=N^DEKZM696}Lp=TBhA{E@2c6)ZnrjQ7rOlBa+0E>du zU2Ytq*8R+a$t=@6K7&sP1NS>m&dAfkSkpt3wg#{G z`PvQjQx2Db`lo2N_6-)gYcu=;G7o(c3!wnZiutYlI}9D50r5LkE3pvp03|GhGWzRD zp+8w=);lO|oO=@WA;u^=s2e(gita?d2(_p}-hW~ROg@i#Elb7d2;Y%*vlNOR;)V@O z;W=dZOcLL6=T@v@!3j6U^br66Soz z;s1(-bfjF>_|`^-vnq${SW2CF5vQ1AW_4aL#Yelu=Uib?wO_CKkN1U+_r188pO@1E^W-S=g^nsv zF5GUQ?!)B;1ijoClfV&JS6BS&!BxyVze4OaXW#?RP#DJ^Op;xqt!r5jKUntY(RWVY zOhyp$;>auXSGW`LpRmxb6&nRAH-P~fR6DGkvW8}Z^y4fcmCt~2YDL^M4h zdGx@r8g5@T@@NI}{(|p;a#-}ntR48*m15rw2OJWHpE>MU_?7K9%Kk3a6A5F=&$qE^ zUT$8SduX<0p|K;t%PWGrgk18s+J}uSqq#utbK355BY(-L&IdL!2$NVue~4I$i{tJ* zhVRLcq}6fAfVD^jfuFztvUX?8B5M4bx=6hKhVNPu%F=J@=ZW@}@C%sw-@oE9Yrk_jsZF_=b2F>M$4U zCHnjt0~adus$`$gr`2E&zLkg^f*6_xC>%QUKP>Ia!_djV1qseEcg`{UucSY1G-R!) zFGoCSn9uCtYZ9R_okx+Zz(xM_x#k5}ez&xVrEsTy_xLq5T)L549TF2jw##l6ktl#x zAOl1sGIxZ!SNOTE!u1t@9Mqz=*Z6>WI>F25BD8?wL_gakCyB=L{bM`SN1-we6EwSE zHEC1vguh9{5#mEX4p5J;7m$%MGCyS32@k6!{(Cmop}&Cs^Jff>%|KxF;c!pBvMWvf zo~3Gqo$7J*9#_lW^%~Q@h(Nv#-j(+~?#J}n0mh@zVgbRH#tKe=pW~qjA6N4!y;YJO zy-(LJ!X3fylokN*vfoC--p2yP;RrMKY-h);`9kN#@)4cc(yzty3-}I*#$$w(ku_RwCx%3fl=ZvT-TCGSWpOHz-e*zeV zs7WXMJ2m$K>ADRb=|h4k#(Z7aAHhL0i>Ep-xfvw&@j7_Gtdg%el7!?}JK554z#aJ- zx-`R&j2^Pg9ogy!Ir^UReSj7<=5;j|fjQ=`S*DIu6?lYJw@mSDE8Pfj0FTt6RSW64 zo&AC*pZeDu`lvuuExuC@<>a7RznTPdZq{rz){*X0;dLC%ip$bw{Qa2PRA%zT_R*S+46U08II9-OxSdXft#j zCFkx84evIOx5Rd%#o`$2 z9Te&f^y~Zkamu3Tz&JZ?$=beQ)cLMjntWJEdUAv3>apGey|a&Ph+O*d1)8TbF8huy zW6BQvPL}zoc(YC)AFj7uhcEWXueVNG&BqQmtuwnHN`qM!J6GA|BRzgTs_uHT4=$d& z?w-5ve(Trdu^AW(fz)@AV~Fpx%MHV%xBG&fFUp0Hf{8;hJQ;Fe0G|g~EP*D2bqJw} z?&JhFt|sjGVHZ*R0VTrWc-C4}yGsRx2P&-;1OYox(<=ObgUYi}U1)nXZ$pSjbH@QG zdU4heS69IJT~$Kj{QGSA$+?)^z1f#9xc3reus2iRadV_%swwbR1_+=pmWR_n2(h}v zb9g8*c4cUL3f9L{lM`5o2(+jcPm8xAg6UYBCh7bd#UDYQvza}EF^WAe-?4)u19%#s z*v)YaotIO`Cp!CBeL}EL-QC&JoIWj2pcx04e)6U&#h9Iv(uCPRS|5Y1i^HF%IuZz+ zRymY7ctc6VLUzsr>+%|8a^L}<%eUuMXd*2-1*6iKxH03pHDl^DCSU4~)Fko<9^Me} z;h7sTUCy^%YO=rsySs#=@c!n%&1YtDE&5aRNL$e{ zE%#n|=LwEW(@#gmwOjhZKjgea7GA_ovrN~PaXOZ95GnJ2;gtQtiS@>jL%xy$m61;1 zgBaROKkNN$v;NXsy~M@4T7qu7DXT3_uOiHd$qtvvPeCbg^o=fNbG4YATb}QN<8d|8 zAzQS%s`1GYJKL%0dt%ob&5|b9&{3UH2$+qW68KZG#w<_jc~{|+l(2v_(hY0f{?H#@ z+9a@ctLISGDzvWx z4Qe$xo(e{OmTAYbX-6y0QxqM>Rf!Ugn-XJ3hDe)F4MWDJ4D*u9r~rv%2!?Y^jwe0k z$MN^of6fJ|^!wg7zhOrt6eP?-j5&2s%`{XLI?46AHvH|WbwM!dOu9C4;qfz%ZK=U) z^Wfz~njqj}(mLYI|Zdz1gcDAAOMOYaySkR+O^Y_y~HjE{9A!8uU>w8`mMW|ak>)j0hUm` z`I@jp;@NSSQ=)v61(sT1v>0^*oUkPr`vy(rvWuCGD{9E>XeN!guWb2AP9sKZmyOByD||Y z`sudBocV5!#9lu6f4FT!wky-aPzJ~N6&7_DCM0GfAS*iKZ%m`eY#xpZSKsBWk=y3s2UcTGu#Mx-b^+|&_+tCrd89)BtB-Pjq`hq z6g*F89t3LJ{%yBKR!@R;Hu)@Y79Xypeg=N_Z=jsy9Q7viz8W`}fp+=^Uh8x^UtyV; za}T&le2TdhM`efGCy9i;z$2E9u9YIlk;2cJX~k8uCsVV>)icG_GL_ekZgO(n=G->fPVQ)@_*1HdTQ-At6Lh`zyAJWd*ub`To)GYtMye98mNNT% z)nqYdDg6_hqFbf)P4qBr!!+D4npha@o2NnJ44lh24}0V{&QpV*p2u;fbs#BlVs(@3 z(-`$W$?1GGI4{BTSeX?MBlB%CWui&zFEQtD#JTb3yiCHlZ`R)^gE@r6K-qDaL9W|2eCBPd% z$z*h*pBY;&#Be20`}QqaLpOK}RAl(q<-Bp!n*!{n3bcnSK6}Wx$F#cr+$fNuo2zP^ z3hNn7Zd~DW>o%ACHWi<$nMD_5mmA0~WB$ev$&ysXoeoDkoNMlgdhTh^QbYhVmMwZP zO}j*8yDr_5NxUh5T!0Z5B}%?Gf;}R33U2>%XJ*iz<9DzgUx%Qp%^2Si-LI8|vi&qg z1kKNJg`qC=qilLL76CTd2$;XZ!d;;*f2BjR?jlS;Sc;r-WQ)ILX_EAvx?6;4O%RQf zv!n+~@)aA??b{d$YC*xSB}2b+rpUz_gSbt42&s*KH1feCQV7$I@@S-nP6i|5JEPe} zl!r~Dz4hn(D9JL$HOx4YO^L8Xw9`E|U5M=r0%A>H`#|CvYF17XDzywfc3ot8sM^EH z;)r!Wy$`Xc+kSy8%SfD-91Mj|y{|BRP`{FXxScqMG}+>6>oRgnt6CI=_p<%?6wnYT zP4QH$Np+C)srU1bb}DfiCqbTz>lZ0^=@ut0!Udw%-=HzSH&l+IIF$G>`_%Wb!jB-t zM)Yw+#7x2C=b?<0^=n86+>}SBC;j+AMzu~3npo02{%j!IO{&E~l&&z@vg+6JgMpA; z0F>sM$F-fjeI<@s@kM0Xrx?^fF-khkeNFVS#6wcSIHg|nL3Sl(kG`m|P_ss#&*yrt z4B=#u&L|+vmo=ha$HIs`b zn&6O6qd&0iecCw}*7z5Nm@!Ri)s&o^4WD%N-VXvW@*qJn+{4pPR`k$beF0;15i8u| zCj_)o>%b^(D~f&4UP3%_Fw}e$`p%pZRTOe7bSSVUILnNjTB4h@}-HD3bBu=+ZKzS4&IiKfU(lyShVGIZ$=8EH~~oT3OK?FBHKX`ehBG3v|Ll~w^s zEiPV0<4r2)$$fg1JpE~4-CeT-$sSoDimFI7fs8aoe(8Yp(x2hTgg16-TXw5jF0Ft! zn6}6i+V%XSdD+JDu#GeWJ3IF4sN%C8@1_^`Y|z0022T$yG6G_;_b+Q5q)wdzPy|_4symRoBh!hR^L_x(XK` zKf;Iw5^QQb{Yk~OtzEg)vVdLsrIIyi7WP?D2RL*{7^h z$83m`(RsEaD_+|nV-WvM`0`y@)-WFNjpD0YGSJMATh0vQ-q|v^tyMW11N*yoUc4lp zI>Y%lTzi_VSm4Nro(3pYp6D~7P?js8#%^J5*yup8XmC&i@3v?Ujv@RfT?8#O_uY-6 z5y%#5=PfB;$A;VeCN}x)=%2xl{sOJ@>J1p51aI1CEpu0@|J0d$Hd1jlncH>x2_Xa~ z`oW_V>Dj76)>K8^L9JFPjX7~wZ;9mTL8X^}SQ-_!a+~ayl=20L$}-KoxClT+Sssxe zFy*~0Y$=WyzhlOcrt8Qn3q?w5N11EZQxJ@9`POJdQa$i+?EVrC>ASyGjDW^_&#DH6 zr&Z>F1a!Y~#qZxcOy}^Jznb@7PzRDlSY|8+@|2v5+4nzWJ??I6v=^jD!Hj!uvcasm z$|L=JVf*GQeNp>6!m3~H8#p2}AC-j$fk% zg|75PNC!M2^%#egMzBHjmsV0WH>T2|Efb)Mf0&DDk&P>AmWCAK8<+2l+~e_yyW8oA z{&8QOu9jBZy6Y!@jd68#7LbWrDTqi5oeXe{YtCtKr&JuA$XJ3i#q>;ncpY#Fa9wdc zaG|22-re5j0fATi@ExfT+A?{tP9}r^(HrHypIW*a<2u_8nK{m622^A(%$oAb@Nv0; z7ydRX(DjUTN@^Pv5EpeEfc1-siWyTY$5iWkSv~?^^L_~@Mx-4}szYj9H>T$AA1YR8 z+weuAB!b90RxCYW_p{2Hvg>lt$=~aHE4u+2uDxZB(`juvP-UK1)Fh}~AdT8yx6`v_ zJNsGW@7tjvX7GUPcc5+-$(N=SX?6&+&vOz8=>xxQdMQHW7m8ImSyI94-zh_Hz>WBVc014wy)7Jt6rRXjOE|@4`kaUVUAP2x=08BPLlYny zuiL0bl9PSI*$%-_E{8s^R!Gm@2`}~3P`!CwPsQ6u@`unCoTXmj=I1< zmew+et!P6dK-EOG#L|-&8KbRN8B&r`KaJG9xaXkCr>g4;95iMS8v{#g=EX4z``04< z&PQY7!&Y~{HQeUten1Sk{@HpH)D7%7eV=qD+r6RD-rsNNgzfSJ(hppG|5c%^&$**h zVsp7cOQ&RtSLShm(guR_G&zEy;PNm@T~Qd$JFlJ;BD(6n$gezbC>^QTp%{Yzis5pK zequcLy(LAVX+%I*fS*Kpw8=~*{Lf1_p|_EeiM~cm$q*u@JNEeza5C^Z*brIWZ`j5% zp@w|=Osp<7m|keNxKiKKbZE`z?73Os8D2Mav9iq$y6>Ou9)0=FXP2$ROcYQx4aE8^G+!Qb{lpFVH+prWO|=eIb&8=<2~ z%#Lmh+a96H)h0t_r^Dtw&lPAe1aJGzcN-jYgIkedStf@BZ@%k9>p?vYQ?Xd>y7_}LTxO{u@>tyssN1-zF^zacqe)=}> zSa~SNqiS%7SlVO@%-J%HjyvMCc`d`|NbRx=l+U}|xKcFCrO2dj;L7hT)<`QRe0)qyD)4a9v3l3pOH91h*Kaq-~$GE`d3Hc24~mQI3)woch)K#%#fwBeK0um^bzeAn-MGtq=}wftgdY;r-3mKz z{zT}wvjGh68l5Dk!H3mYEJVlSrCuDaLhFuPsAX#)8wm=uQjSxWyHF%d62t+tRxKDJZe|-e9A0rQEa@XumoK0y)>It;*=|4*>hy~A| zNg%UP`rhV!^U0uD^u#>H#O%#gCMclOIp6IB>yB4)NPc5V*FaF077`YX*|>t*k!$x? z8M8k0ZIac9o81I^q5=}c$nePCo=zx*b!;?!(Q64f|P7ZtIk5!%JAw|$%IQ|i>Ms*DEx#AlDrzbH= zjgrENOx4xp85%k)i+oyNasZN0BLr_OYW=6-(G#zOx) z#UP)W4G~P7>rMpWPDg)`ap`7HOqq&dE)yXiJ?}6qCc`NAICSMJmb$QUS=w|s+qBMP z?20$?}A+O?aANhkK)vS&BaLUFN6%comi-WEKs$pxGi_P6HwFP5OMB}H`$u>MZ zV5ZEBg?l&u#KxQyEbC*M8l&0_bqGKk_zHfxzw?^&%?~8V83wu*d2;n&t=0XK<65^BZ7OA5OagKHb6UBg=R9PU zCB{xn%t-!92;PW^JSxL1M0yQ;l7xj5wQ981r8g3O>F7L>`&I@DpD|jCoXImFn9hf3 z_m53a_9SxDhXFi2%R|AB{4n=f0u!&HnT^Kw|F|bvLnC%4AZmOMP+b;{yqt#3umB+{ z@P5iU4xj~P#c+LnVZ1_^$+a=5cW5{tW27kZ=>R!g}p~ z7BZxE=;JY(0Z$sBbGK*_v?_4>eVxlx4}{Vu`%_{}Mj;?v{FmuL+E3N2A(0CK8}^ur zgiO&^6-_tYREwAD=<@H)Cpl0yHVJcE2AHKnPeAY0e@mtj7l8A%zJMN}0*O;feGfbu zXM*K@@!uUOz_x;nw$&!R!HOcz!Bt-*tdwJbL^!sBJUs7Dic3E8#wYX55mKz1x2})| zHI7?kTaLiHv+V66B*2G#6j5nh(Zc|QBQ(KC$xJtIB6%IO(~&W}hf@S2mgFRpjV4w6 z+4Rp*bQJv%k2(2gQsqO(T<|6I>QbFYAp{Wx>SAcs>mYshd$VN!lwWdxs9_|O)MH9YX~paON>qp*^IY2L-tbHjopWZXeNagsOQ zAVE2pPNO-fwxGj0n*Xw_Hvj$$lU;yiRO0PR6ndq@GFP-XDc`I8|DeewrRxr)hJ=UnqOEH5ro3Ry+yZ}Wxj}l( z-{^_HumYpm#RX1@^2zJSS}H`pG4wGPLk+yG4$&4*P7u^X;q_E)Jjo)D`^2TZ@!t;FZ%E?@3TB~nh6E}U&I zEV%d((g&dggO$SZ+@-Mk+gqE0*b<=1#1@`RR2nDg-WU65Pxz5{{I&KGx~j7B!M7C= zju-u6f>#HA)Y5>GypS!G&$R}2{=P_ z2*9;wKWb%M1Bf<)gG^u~Hg^+>YFpu%X?KksnvCv%l@Ck=L;vkfV@`@H&OmdD0k-8t zJ(|B4H!h(fdBpD}c2s*S6XRG)k)SFi#fRk;lOZ_tlhNmKV0n|@~Z@*3%D#D18VT;Lc`?I;~i zvo&m#o+dI;nXrJ3YN=oVvq?|Ss81t9t{tdEo1Ev|*R_95(D@B%e=7M8YI@&5N{O_F z#KONCEoYQb2mwbtEueUvrVW$DGRNzD&ZTv_!NOi_!&3WGV$p7ng~^kK2(qHQS3aUy z;jXMsGTu|nN&SS*6f%LoTf)xQJ5+ByE~FC~Y~jKNHBJCS)#i%O&G~$mKX_w)p4{j|oL5 za{@?2>DoVabxVeq-0l)<>Xu?|k9}GkncKb1b}u(%XRt zUuv&MJoTj^7uX1no_YO$RTa4kSBa0~O)>oOkZd@4v$%pz8o_{JL!Pn0@c=6-UHwmU zW-OGdn}5RuADgw2UnfLX$m!G}m$=8Hw9IO?~X{%}BP>VqmLi`1+k829L4 zn_#VIXxEBH7ySPmEbQXS>M)?_O+WRMNt-@tL=~w-w0lrjM zThwcV!C(fZykA>kn1=pWgJqA!wY49wzq+`Of5y{Pm`=}rzT-~?_J!cYjXPZPl}su1-P*Dd&L3b1zU3IzJX-Y9KV zKm5+4f@A_Fz_*#Mx&3`OCVx;UZUw3!Eu)mb0-!WCHU0RAzA z9%RJe>w5mujBm+w(ul$R;}wH$K2Hlx z6Ro|;z;hq+W)m$<5x2{}shf{^9W<#RaE@-P6~~;k3;l5xLCupio6S>AZj>N*$Q7+Ap@<&@ zznRU8QUel6`-?Lm_q9xn?xp;%971lH#`Lh<$*U1OQYnuhDo{ammr8vdWF=02?IAoj zgul^)5MN(jUT>wHl-JccU+oM+824f(L__W{O$GS4k~C-O`CLj{ZR@mm_6ORI$)bH4 z#u-GwVVZJ>S!iGLjP{j7EmN9o5Qfax&7S{5)q4k09e@AhW=2LvLavcALWMG}d6Auw z%2sw1!nOCxs7RE=<=QHWC}br>_Rh@an%UWY=j#1_|GuAp%YD6G&*$?z&v~5le4NMA zsy{ke`+U5s_+9U#RqHn+i@a8P>zXd?9$4U$fT7c(6*94+hn5C@*tkydD~78^!50n4 zfM}*;Xl&e`DB3VEQ19eK~#kzy{8 zak@b5zM0Fl(_M0+Y6E^H5qUMl?y}kbFR$62KeJ}5@4YqR;g(ew<4OSFPT+%E)9Op%F06D4rys^ zy`9?MIkGUKLUqq^Aiue#TP2ti8WfPfEgqEWc=6N-mG~{{7mt5M6&rBM@lBZw!(>S2TIjvz2gw1VH);ArR`$?_e=+!u$o%r$ zjra3zZF95q>pgw#tzD06&b?pyw6&M;Jj)r!C1qtvcIB+rub&a~(DbKBZR%&&8f2W_ zjQ+XQ>cl1o+*fhbj6p+`dco6pV1(!g3V9(89d&OY2#9uFL_ z)_qiDCi2$4?D`L|>mO?QS62fQv%U~-EUpU-K)u3h5< zV(gAxUoQVMkHa-exhQ}5^y+s+I^l0bdCT&9eurJ!v+ctlyQ%X@q?nA{#Ik>L7AC5R z6(605CfeO+H6e7Wc55@g+4<{BqL1$r!pOWSty>P!l9egn&9qu26q9+0(B5R1Wb#^~ zz2=`T8;lhPlbrVPozN5@!8j8kQQweBdj_)QNelz)^;#Z5wKZU+(k^~FJ z>RR03|Gc$HlPA>JmLJYGfcOvbx!8U3+UvyprF738o5LVPoewMDV%`*aq z>idG!Dg#IbFzG)U8suiA?3OB6d5pZ>R+b8xSa^qQ|1zH!qc!hEbu-qU(lynXCE43f z=}`WYI3-QR(N4Z|g7-N$?x|?UPW3n0jv8hr==Bx|2sncCAKcuWjD{4tib%|qt+hqIaE?D-E@waeI&6HL3L&8wdm(siFp%iVl+Qkm$gkCWrm@vw8< zoH)%>9kB&8KMDff3an*=-sR=>^!25PJEgR|N@mUau$;28_^#VOSg0-equ|Xmcg_7Q zIKy81`Cr(m6L_2xPU=aL*iJ@%(Esw8gDJz&NtPrLkQj(ma5#@$=QC6Z!SDLitE8l) zt>EQ4I&?7CkFMT0O}+HJ>6HQ9bmuBV@!tASC8=rsXNM)si1wIDh{icZ8LXWfr_bvFnb3R14BS@q|3>h`juK z+txQ(A3w^mBP7?}IsMr@we|b-<`;LFvQ&OCZ$@xR&t8G#!yTe8Eh<&gOz1n$z{?7vQbhCI!@ z7K(HLC!j^q`dYZQwUzn-F`OEz@Xofl>$PxBlH zN2+0wjl;mF>#oXEJ-xk>5)zBQD}8E$Ngwp(fvswSdQQO;k;OO=-pR<|-k|I4?d@-C z;J_uz1sw86?(grXB-3W6rHzBA4w>|3JilI`<>7XN-1h~!gE{RMX#)Anl^gW?CBB=z z`rSF&;Z*I?>zbaP5@HYV>!qvhY5n4vBuEf3aBzH-^f-Oy4A~fI>URjKqdtEse+!fpsFRaKRe zIW;w9U}z`|G6O%peEIU?*T~2S0OUtadw;59T3O3s<$K~x5@F>*;Yq@GyFmFyJ|zW3 z&}e!bulASX;!N>3d>1di5qDzuy@XfNdCdBm6HBaZWN1inFX8p;QlGz$U8WO(3#%l^ z4v{g@J-p`D<>HtZFJACYg#>Y&TWK`8q<^3{#$nTysrIq@5c%KX+E~9hU>n;L6%x9U z;Yii^*!O+;*RM@%_D)XW9J!Ag2M4{GvFo6!m7*1Nw<2Jt`7WpPXq8|8U;{M{^iP;% zi%Lr+z9Y|EHjDq+(bJQepYI1IX!i5{>^E0*+1|on>NTYFQc%jQIRAH$6Rgpl8gZb{1x^Q-#A}v`Xgc z)m!z5?95F08Q8xBDFWQwiuECJvv5XvtNKGzbMtuov$NtBML(8F1Q<3mZ}SYakAMDU z*8S^Oj_Jr7?m*_;{iB1?BWriXVvs=^;~VzX(0H^?qH6y8s||5HH_}7yLP8|MTebS*Z1XE2<0{`B*LE2) zrx=8kvTZ?ev9B%GrY%{mi7ryQiH6)w-NvqNCsBtkGPe}a>CQ;H@GOMI0$a<)CymPz5tf_U`~HTpJM>&%%mxb1As zL?W3njmwoJEbdE#*Vd{|qipYg_1zKa8S8zoKknx0D!Oz295?s0Fp;c}#Qe-mUvDod zo4Wl`tN3Ur?Pn}DFni8n=u0QBLQqvz6CzO zA!)oi)Kpao&IjME`1$iEnhUhCzB>i%8H2~!+Hyzoc65O_oSbbNTUVL!`WVGyK`jU9Zn9G|fvg3OQespa1LPf`l@rA?J{rv{k zEO$PdQM0sRq;mWlpvj|G|L*4Bza>RQ;Gs@51;s-$a^)o9G6gyL^RnR}VzRLgaM^Q= z+}zx%lK%VKxqrd1)lajqQ1q7Ab@L!ET(}U{(nS2_WlcoMsDF~zGDz=ncGlO2X$9$V z9xIqBj8Y^tKZ}m$Nl^@|83FfMn-;J?$q`tIo=Cs`gj4w3IR&2eV!Q4X|6RMUY%bwN zw(T}Et7~h)bqFbUDkEB24%zc^r1G#=fQm_!NN6a9L>_K_3?RknjPmmL4&nzm^ z)7L+PL)9WAiw`$){{3XcPHqNsh==n z{!6iYMm3FW^=X9@bT67fc(qb5`+sE6C~*W+-@|3p(jmqWZe1in= z_36_mt2KsMb{TW^al|`XBovZ-Gc(U8CY|T% z{lD^QC(#j&B*lv1G;bU6`eBWeQ&XXLas8Nk*6BQE*6!}^;AmShQolw=156}b?#G^X zECwbjd)T1`$aX#Fl9LO_#b#LCe@ofH#o#j?kQ)=lK!{QiVmW(Oam2LB*N~^OstT0X zQo|KDI(E$im;?F2mw{kpj@}fC>`mvf^cthqI!}hX<8Ucgw;9)J3G@ul&xlZU6c!W& z>H$md?YVYPd%F;TKVpOE%77IC_THG!-pY9Gq_d?KO*lvuwts842@}=ON|qqxXgzb+ zS2#1;@;sKfku)4~NIEoo(tl;z_(S`^dA?5FD3E)aaMz!OT$%IK^AeDEtYc8TTMUws z7?0n&!dzFbUS*8NdtQjK=!v&_Ih@Xaephl6VgRfa8VJToz}A+yUO)Kz`t|EpyBzx} z<7be^n~Xv)ab=HT`^0Y8>({SWjdxbUXg8~Uch<7Ad2GCulBK;2wR?@xX+1`wwLi-A-L%Hf0W%Ki(kT|oo~WtNk#?+Y%<0oR#aAogoFe+5u}VU&U~G$CBQ^SM}uLr;(j+ZT|^=WM@BFv z_d%4cg?flHBrD5WU0vPGjGb{Crk#3?fPA9lpR<5*fNUG=J@%Yvw(PN?q&&H`_JG6g zo*qVQYj?LR1n@wKcA3A)&z`knlZB(O#7M8T^M;zsO)mvj%18dm#BJcnp|IZz(wnz0 z%PX-7fy?Cvmv;v@ph<9QrMk|h2tbc|duc{Y)05v4pe1-xg!pOP2ESf*-redHc8uu5 zj=9Cf(0!3J2fGU&t(AY%ong$k>&`|zww)q)IGO;cS{k9^H#o&DWs_g)9$#@HYQK3L`a^{PV`o^UcZ-Ta5qS+NvRF22@;4TbpFc;jgk z+SN5l(v@eUeDXm@;hSLphF`zLg@tt){NE_tjeGIpyNR@Q`Zb^Zc9paOryuxlWvlrk zE+mwXMmCctp5 z^I|u^svl2TR?htIj{*MGz156WE%scUfFDF>9I*W-Ti8ww!z2IFjm=G4zzBVmsQ<{d z;kQ$!NSVnpM1|twVn)Zv!2hVh!T$eYeZ2n{7C^aj2(k#dSgyC9KlgtBJ^>4=-Q&Q> z#PoeSI3$Gbzk_&VmdHk93uTVU##lWQlM0Y>^;)0t1L@M`|P!Tz= zXk(MTD=vytZ@3yE{}W8K)@KT2OCS9DaIn^VS>R4yAlcD)I0r-}kA*%VZ-GnKoGgps zg(nh>tNmsEsmI;nQFj$!@V;wmIs*9$Ae(0i^B4K}JeNNI0_*|mt}o>F$7ywZ^gozD z_su6s;(;>}WhkzE%ZBJ!s$U=Pa=3M{P(;+#)m3cv8Cy9;dHnX8g-pYH@|k#JKGENQ z`BzdQ_2e4<|u#g05*|21NM{ z8a-|@3_uj8k*_ZkySlp4KYj$aF4Z$z33GBjl0YrdN00H$nW3Sfvn(vfGK7oAFCxnY z=eljPwK#Bb%dXELAJDP=(^4$F*UWFT7c!SzTBN9LTWB??3C2mXLe!nByMEQBBPbq1 zM9jYV(BR*(F07$gJSk5yVQM8hi$jXAUijLSFI_4!pT}u$I03#R}6;Run~S--uKz9*qYpd?%-% z@S4SVa84)Mrg*8OgGtd0O%iF7*ziwV9?#9e;#=_F z`TYI+1<3x){CB276;sHh^wfO7?+WPDxJ#C!}}LS`S|WblmQXQ zNpCq&27U~wyu#596CTj?oln&W(Qn#)x)7LOP~|{8f1e-nTp&N?s#gt z$&;B8pZc}{;>9laq2?U0+noxZew7++<~4mm%!K(y{@3c6ztN+0{o|&ULJoy7BFGw=jWqMcObJZvT5!5^(&R&tm~z_Rcx%k zK`nXQO3|KXRWCiQOM4{-RaCW<*vC><`eyjg<90vy>bhhKZEV46ATG|Gr{HamS)3>UUXyMhT z1d%$@(|`98xpFEiM>PL_hGgJNTPq|CrO%Fw4FKD{H*hB}S3LK;{$`#8E9&%Hde;+N z?&>dEDoSL6i3^3t^AIvx8!IcAZ|BJ$^)yIuRvYg#qLKDc<)RB}1I=c2j=TOl)`zln z;>D|>3|eDf8p}}MIcrYM9HM@4$&&?h2GV4{ZE9+ID0cSXT9)gp0Vj10jh(XtxyD`J z!%k)eTp~c&Z^lT@FpK#-03~D;D!lV_5aYF@ht2pjaxN1UelOTHyq*Y|2@BE<(5jXL z?AK)Fwzjr52={hOUN-y`N=M}}oe2&tUhFs2bBKK7t7>>tJcDJ;+biZn8(R6%5fTlqp>zH_|)|v!3d{nilif`SjWl9Nlh)X)#m-X zcOnOTd!RWCb%zAGo}UYw_*z6{WaP#X_{iMpSG34@(|DhKO@=_4$cXv!(Su7TbGFj}^7LMX1?uyHQl5EQ$IoT@7! z=z}A-d-TI^Z!6!VOyvGIR|UE|KR`|9 z;nCrN@NZ6x-{yR8R4{cKYDFvf7&VyNn%KVIwem=nKd(GlhFJ! zF_FK>m=;BVAP2jPCRDPDOGvOS8UVHPdyPQ^$WtWnU+eclkDDgEc6NQ|z<=rl%qS;3 z`Lz`;#K|M@bn;(IkjNv(Ho-3lF; z{O`-y-O%Vvs5NG1LoM-~`2QYpQ9z(*XenZr0F0MBm;jj#RR|S(`**(<93DJS{NFRx zdVc-V*VP?=Byp;p@IRlbnFV2|WC#-*8{|pzzxQlzZoZ6hPc0!d*R@nv#<8=FMPdXVCwi8J7!M;Kju~dB^R6Ce!@4YR8k0Aa!m zeDzR}i6=a^RVyQ46;R6Oq@~r={nfBi^qoW_N4*z>r2YQ=x4&1VS+tAErG&^oXxI~C zNkVdR96^#?_@#S(`<9+wZIG6=br%1h&Od)`t*z;P6!O~~yHN5^QMc4V`S0BRI{(we^d!rO_JYT3*xrZFjwG|@s!vp z+tA_k+gw={bP$@zQlZIlh+!*P>a2bHM58{a_@JcWM3-fQe{0udQ1Rrdu9P~_+r3CG zyViiCdqRpK`VS}qoL^Yx_PoMlAl*fs?y9! zS@{H!>pwp-5~x-4nmdny%7Y}2`;5oqLAOlj*$2YQp#=-*Cx+oMQd|Z(DlJHbVTuAR zv{yguVly+xk|9W4bkmQg=6RcZcssIh%lvw55ENU@K8j08l|yP?PjS<@Tkr7f_2Yfn z!9vV&Cld?RaeWh$MKls_EYNw*DHGB*v@T8zCiN*-Y*dct8*;_`V4Qhpr*C+8an%!w zW2as-5a9^m54x`5NNf=ry9IEl0U<_LSNG=4E+_!%_+veT+J-Cc>_`!#H{K7tJP>Cx z7H|Dfr$Jp$h8A3?rA#sfml7%g?vuQQf)GuBq-<=8ucarkBSvy9A(__A)vOp}Z}|IH zHBW<|Q7QyiOn?ZaktY**EY`|?_OV;uR4wI84M=)u!ZW~sK4t?G?9e4er@UV{@(H<3 zKO!hk)zM zV{*xFZvm(dl$J6-W^2S@743G&?!o|Qknz8rgqIWt$HEsVqsVYAtO5ycf@8H4pR$gn z>%}D{S-_aP18K5&U0)slfX8u9SeY#9ERMty@nE;WAO(Md#TLD`=0Y+Ps(Av*<8H~~ zk3jejrJAz#Gj~q8QoJUA`NdsjKTfs%`$%)~8rM)lIxU4=)-x*f)R3FzQtkZWVoPJA zs+yXelatCope8Z$uSw_mdzgfjtHu=KwZTEcH=At{bVpOM^R<6%v$M2O}jq`(^l1Bmw%9 zM#!J`zgUV7aaEMN42L}z=}phJNa_sB^9C%AGlrS+GA|ccDv#G%$PJr%c=-Am@Xl_CjxLZ!DtJSi1gppqqW+XWI4-Kbfxm|C%Q;z2 zM!Am2k#IK>^tt5ldhz)>XLhD0;!N#pl||RSK1xBIp)xtF`8DD9G+N4r03pj35l(TI z6-|=OLk=rV{uJ4{Tktqg>->7b7nxPlt)Y)cE0Yp!wF+K==-j67Ukvzodzu<2MyM(~ zqQ_Ax1wWn=(6o>sXUqi1yV8)$XI1zmmX(%kEgGIWviH$tXzkD7C&yb)XF(&=t~Ez0 zgnj>K@^p6L0yFG$E;<7V^7Wu-&z_>X(+bZAu?(MSpAchkptvYEx_%h9YNT;$ny~BT zc8H6FRbtYwrmS#Lx$%JJqo~w66@Q;Yo2}lKW;y!giO!$R!R%J$N5+BCYcI?c+cmM> zF{H$4rNLUN8Cs#Tg0eWJ!dGLbGPI=o=#{(zH@H`G$J;*pWO`&AO|vEP@gkc~$*Y{~ zpxDf(kk7fxh@-?3+hM8yMo${>x^pl|+4MZ1SoU^hl~Snq_j}GlmA7-4e34jHnuV6u z2c$S09lDNN{}vHOHH#BR9(@8ZB_Tp~p$=C|AEK2hE4ci9f}Zx|G+|VE9YfyK_S5aX z>c4|bfzhk-ynMp1>CwA|c{Ggr9g&^k^+8%as|8yXM^*&r3A*Foe1zWwYrUq|xME`{ zi))mWkZ(}MW`sANz{w*?5M`wdZ66p2F#Qwe2;uqr?8iUJu%c;-wd{#~OdlBiR-QM0 zYlB^EKBA#cDIPbwe&%{n9S7`wI^3}6CB6lv+1eKbh#lEHM1YoM!@yRZ2dEOhJATZQ zs=2u*_jRI^LsaGILH6N;v`3m!AO zUERPh`Jje0AMbqqLy}l2#U=MYFK-Ya7=okk3A1m~m$cA=p9<|8btTHuamT3o8Mw6Z zHH5oieY#I|bT);Dtr6G>u9lARJ(NVHg?7~@rmEh<13zrhXDK3m#-y8Ca|f?#d9#X( zg$kzMz9Ba;?K%{MoQS*Q=UEcO1z!U)_fQUrvV;0a7hi*ivj>Q0X$C6P9Iy)*>D2gA z7!BjKPxYp`E~PN@-Xuh$sgd-L+0vQ5C*+3Qe~g%Bupa?Onkm&m@dK$2;u9Z}hB*Y4 zLz&Suo6Gd9Zav4l-I)ZD^1IW?cQ9^&!{voojH>QW*k*Pbg*om z&T2uw+F$>*cgbD`!`Epj+&*OECsyPt4*g+-o1BtMtC;u%cM=17iVCBv6r6i|Ad3K@ z`UhKUi^%ddNSiQUeTFojHTT=w8A8dc+`x|^)9?MqM#7NjYPuC_aoUquOZtxSA$=Qt zd+!0FZhQcs!rh;!wU`|-J(7}qK=BDf3*TK7%_=JxPu6IFWq9Qj%+7}Y)z9}4yq6$K z)dse;Fwo?rxg&1-84zVe7t=l&GGv=KIi4{q@{ZWabowhDagxo1NE>1dOZyL@`?vbd zA>62@Vl$r6K4@5wCB&)j74)}B9VYXTAWr+@J(nX&g=aVvDG~5R(!w;61igKQ%eJZH z93}E40!z$7fF@B!y!11$$cPT5)o9AiD8^4q7ZSy}|1;VP1%-Hr%3= z+s`>3vZ@@G+jjq1@;w8m_EEvW(}B@kC>6~UY4YJ5-+Z;5G!q(Lby1XiVp~qac3LII zFpy$1u(S(fV=fXfogRenKxe7-TBY1^ybqk9L3S{10oZYycxxoFN8lTex2?x@jTyHEhiZ@f2*M_5yPT8xgo($sY9ahmw&(w6urR*{FS>+?Fcd!K!W{#KUQ}mi_o;lOEx3tvDjE)aG8+7sHkOC&SN~ESplo$7W<}#A%>ORCS z35OLGo5k2flC);ULjBzLFDZ~qOG^MtCVxCTD~5&gQdN~~&kY?xBMoe=CH@V;u1nq} z@$E>HdxFBa2t!-jrK_^I+$os7g3?^&8|vy7r}J8E1Ox>aakz++IMtZTcV6YTtS)Mv zMAE-tUulM2?Tt(0s2Zg|Y|3D^0bGQo@|0Qlq0UBt0fPX^PeRORm#4_s zy@D#i9e%0nv$Z)IXWb$^rwXD4GqfJ!jXgIl&AeX+t}7EXDvnsV{f-(L8(kgy(aC$a zE?)6081o5?PI3*sbBpl}6rgFc{P34<9o}=r0{*MJ?RfL%O_(GPj}lo$ z`Nnupo&>v;H*e)_AN(Ch)ui2A#AYnMTyMYRJP=R*t`6J%`?r~n&bOU+oT|x=-OKW?%W)5_l3{`oQbK z3~YURL*>I~3;%=(&nr#iEgd*Y+aVDCdV4Xly*0}S;BFc@lBMAJQq9HF2$E$D2 z4DoJNrhmCO&=P&){>?$rBnyi}9n6KgDvys{HZAVa3kG71V9#xz4?AdH`3AVjkfWSC z!v4}L&gZ;xW3?$}fiza09d!L}>09Q7mRo+6lF*M6Qlurp!8Dqi5&s{@PhuAJ+2=cb zOn=sCw27#E!+Cn`%V&HNg3qbybkN1=>2PM~MbMI?9s|(vGpG|^d~H5y?r=1;L4aGC zNG2mhsDWvBu&~ZbGaUy>1_t(o z1phU9Z7ILWX~b9+nd7j5WgWlzi`49Nh{FCkR<>Gb8UY4JaP30hF=~)Y9pTovodS<_0_Z zqHj}xxzI;HWiZ>A9$evR;4))g|LGMc;82y;s;x;r1;a?^^MdgNXo!8ueOz z1K`2uL8#deakT|n_c`tm@LVjKt~*{76Ac|m{?EEi?G=>X_1pP|8(UZiJ$5_ZcpS;i z`lSLcy=3~h%n7jA>>3GOGY!X;{?`7foJfaVErA~feg^Pj(azEwVLaw!HX1V{k# zDlL+0cyQ-4Bo}X{q4Nz%)Db&ZI}xI#+DBp2gL)f?e(-BtWqN7>P`OgmIRb=hFwNaR zB}!~P>-juyS!U4k6dOUDm2WF6WiDU745zNVyt7M5TG9`DC?5P`iITzul*&HJH5^S~ zfZh9f!zh@3T#j}NptBYq$14J;OfgV;=!kKe&Smiqk>(c8bYnvKP3TE{#{%lLC%CI+ z0z~)hXQnwQv{rFb)+pNaMvKP;h-dODgXbCSX}omwo+lG4JpXVieC+Y5oPvS^xEb$D z-q-ah0vtq@2^v08cBUb(h2X@l2NWkr5tpto$!+CaU5G%bK*Df=q$`Oe<7tLQs0xbq z(pK#C%1~N)s7VxkK3n*KQW|$rPb-$+jrVNS62un4 z%s_SFK{Fwaj{<3~aseu3#9TBW+-rf@s;zu*D1djduF+FQ!RCGj!_I)%Nbu=H6B$(7 z!@m>H>Fje!p;Uf{ei~RosZn_kh>+Ij_=C~82J*I06|lUKd0da<c%L4Ts+xWhR6G9vtRPk6n5Z+}0|rEG0|-N)NoC!NzZFj@j#mVDZS0Qt4= zprY}O_`#iSJzE*Vjv%T)2{xQk6EgNkEoJ$udzqk~9EwbkoHa=?4R3x0g?nIbhS%zv zno?^sS)n`1W}|orrz0&~4|9ygZTiT#ykAD!*P`9P@g>a=i925SixDiFf=JhcsJup(#ubdkxgpLJj9 zP3LKnx=14D=)g9D1tq6nu}{NKy9?FeX3Qy^Rknay);@S!pSP10@RYUm%0p=;_CXDmjoP z@bmKv2*9?^U{m-(gy^1axxyIjbrMjm0sYB&3e@r94m(E$y5%k6>z`1YkKcjf<)=^d zDT0lBkYZU|1x6=s?qrjz$qDuU#*m8)f=2D$gU&Mu__c{rBkC&@i;*Ufw>dH(7>1Mu5`1JR528#FidW9@Kk7 zv5G(k{Mp{lI}CRyD@KOlE34$eR&O&h6p8D`8y?I^(E+U3Q1apB`LqYiC0YYb&0fS^XTq`xHb-_Ca zgW>S3TOa5Pc70{PW1!k9SaN(U^Xc4#PE0E6cW8eA{+g$$vY+2ULKCc+t*#13M{ za4IvL@Y*uI2u>CaXcX#R#l>Z4Ubv?D(>`iNpzGxO483Bc|Jc( zF$x+h_*mPmc`hl|wlj97Bhq{&-YocoN~wd;y-qNk^45;Qm^C8YZxAM2*> z+`(mSW7GHRm&&dH5nahp{`~n;U{wcS39qG-a#T2eLVhpMi{89>7e4;OqKO5j=qbAZ zPfP<`_WaK3LKYG3)B~OiAW(z96&6bCh7QvCd3l))u?Y#`ud)9=As4O%77}Wmc9}CT zb=GOptqYaUoq8k@Ux~UVA3n9d1DBVdJ2+ja>+UYr`ED>z(W_C~uyCmV-`U95h`5xL zWi)cLOg=gI3!7M533MortE(eAR`(9v%Vm%}&5eyfA?z8m6T5az_vfm2WdsE!YL^HD z=Vic!-vD}bZ*M+35`7;!2Jpm)zK?f5-lERx!oV?X*Y$Vtw0C`1NljhhkWHz27di$0 z0%dq2EK(Jcs-AO5wrDu^_4^R?9xId+aL@il!=&!M)@>rhO;~ZX>)0GSP*URG$X(8d zwE1gWU7b=LNf>87Y8E*4Y&>1K&J4o?ts}XEjqdr+9CV0)y{#qa4Ph6xq4K~0p<#o? zHhJ}L^^xhMKx_t32vdC5eui=fYvOx_?Q$^gLk=Z$AL?Cv=UKZtff>ToDdf9SLk6rW z8x<9@-z<{$_wQrPBsS~e)?@UL1iywPI?27f^2?VyJL>A!I=IZzY#B!^0)bNNSduiR zm=xYSFaT$pY8@u}`r&^e^CQIodp_WVHqcJ*RP6kjo8!6}$$B}LDPuQbs~Wfl!2Os8 z2FmM0twfRO?a7#cnMB)-6UbZZDQdOLa&d1HeBlPKL@m2dpl5+q5nBIx6!6(u8+gsBp-OUltVvxQw0y_1CI7M{H~h_^c!MR zfk3~|m;Yo^=;3G;=F+5!+-~eSL$1F5LQ7Jqr)-jIz~g4(7*7jlw~(sX=+dBX$Ru7{ z?M%lOJsc1b#xH)aA={WJpG)ZbC(*g8};c~({Viw zIBp)Bg}bbgupvT%X{MPGIYmY5wX|{-OJ9cWHw!ng71{S*6b+;qF@W#{d=xFIrK|Hh zranublRJa$fHnsdh8(KK$yI1COA@;ToqIbEe#CGDj0bN08EtQ3J7`aJQybDySI-T7 z#xC<_O;}RW3+QqO#BtoX*{*fbS~KI^ko7-ks2JkS*K`&(AlH?%%g|y0H1D1zsqs*T zuI}y&6S@Jg(a^=vudh#pPVtyYDL&Wzrc?3254E?5j0C9et5 z_nU!OsV0SYX4%RKtj+6fQZaj5aQ*k?(==blVUNHgtIKz0l7LEjedw*4O`XbfVITQC zAv(5H`6*yyOFEQoPdtH3=r#D{RSdp7Wbt#`8nEqlpIZ^``o-h?XjvWK*`%_4__sP> zZ>c1PMRZ1Lbf*o-!2>kl!CcEnlvH8UkK(!Yg_aHUT48GshJl$CPdn^Xk5zo_JC~IZ z%`6;T9kaoY8^XJosm54Z>6LHL-q(=i_;s$1K|7u~gJIrqh5BJ(e_Wu1Go#Fd>wU6J ze2@;w$=$72SNH2M*K4(m6s=#hx^d$Mt6cZ;vSU-g#c+EnVltc(2@<-!#{MYwy1_a6 z?c28<9WPxo2bX-ym2#ePKI3qbLK>R%2<90#fFj>+SH(3|SKx!S)Gyo(J zaw!~#opEa2=P;zik^1&GsCcV!906zeQw%gQQjHQQ%3Rsq&XGId$GtQG&T(*Xy-1?> z!&{_!cayJ#@rB-day*M1yQ&ejOWI3p4rP1GAD!Z}T&6^!Bnf>ZyFa!4&oLV-x?MYn`ucXmRqTPp!w~`Y zj9Q1ZH8tu9*sqG41P*Dx?tB-!%z$=ramkHSJc352?*vE*zs-faM@FpZTszs>WY_x{ z-pz0Mh><f@1RlHa3E5|M5fm%iPIJH%f-&w=k!jB2?0lMoSXy}J$mklcT$yBqG1 zliBQvSk`#tkVey1{Q}5zi9>5Z=<9qnx<4bnn9(pPxz%sUa* zR8&M!*PEYtl@K8&Bl}~uC%!hm{UUYKE2h(pkUoix(1yfY5@I?tR4$*ID{NptNr~*K z>X7SsH41UMfg@ladJ841d!g_3%q5e?*5?lP_VicRmDr2J`@xM@L9Uxa6jc7Fv>-a>X(@>;ql*GUhaJ=1uDu0(dCNkUbJzay_0D zyItT9s;=TXtP2oVng6~Wxo0(RkLRfBWowr0&cD}|oMx2OgvQ^a$7dby(M}%j{h8I~ zNgzgUT#1(5y)Wjgf@RQ_j#XFg=f6S^yL<)f(UcwOj8&9_*tTtuf-^>HbYx)scjs(V z{k}-V@w?{cnYgxI+gGSn+hyEfzd6E-3)TpX2E#k|@az1$_kj{W(%G_L)8=Mng{dbD z5-SxGVh)2yX#o;h$dMOnRs0~=%XpoVqLpuHa<7>U%gr@ssDJCNddW3j44QuF$7h~8 z>K4;)9{i7>3!lkv4*-U%@oS%P=tGDmLaR1mA}cS%dk+;vSj z9DA}pcW2NQVf{!Zfbt| zUl3r|X-Mic(CJ9yiDVUQ;&aCg-g?IxL^IM3q2*_#8^pa5X0$>1O#_2Q5343ZJWfoN z{a{*t$M?eD(Ez!#S1venAIAzwtl|YvIDx?<+qkT{GNp3gTzV?yZ2DR%zkR<Wsm2@#B^rsz?777sgiwuYQ2Rxgz=ia3T#EtiJ4b2gqndNr2dJ|lfO&$<#o@rCH4sW|Pf zhVtdF0yw>#s1zDieSz&L5pMu&<{xiNjJe3qLPs&e?>uutT-H;|ZDps`jWr4eo(`tN zTLyD_SMDxCtZa~79<5G|%5HfXOWGd15fiW<;@;KmtUsog_GPm2i|KXp~c$Lt0Xd=&?EK~5S9CDDUVAS zzoHr?bZ;Fx+MXHbDn9bPD2{IgO1$BO?{hfX8=c#6e&4BcNpCLAX|AzHD*(VoF|8@Z z>KX*g?`BCGHMPNQRepQ88V=VMVRTF@u{ZpM;_Zxbk3ZSJ<>4rf1gAqA*m`f4hYTQC zzU`wX{v4ojb{}|4vp<$ivoTeZdjbF)-|{Yds(~*)<3+=}fP3dDr(cNuO&7-L$SA%H zIoF+#%YrqD+7lVrSmx#7xmh2KYO)WZNz%EoqmghiiGZ;j2LKXVGlU?R#5bzUni|wTh4T8AefSD^SmxiRMHV7Z=dXj>e!&Y z_Gte7N5G`o9EQdG_JL)$8STRN_kF7#%s{83xo8JCW1NmBs(lUbrXQm&8q00h(B9`* zHnt!_O2u*q?A`=|q=tq@|ARPLPXh?#vIhkWewQ*6pb8MMzLd9x;erQb)yG65q+Si4ymYSai^EX= ziZP8J$s~13z@gXfR@L6JCC6K@bmaz{qDb3N+$l8lI_2KZ66&FH9)CZj39Rq6X1hu7 z%x;uO-TQ#IhkK;#GvChEaY!k&XMp699Gy`|E9G9r+!(z|KddzLQj#Y{_-wyFXe#?s zK$f64?FW6VzF%oCzB~0+X^ofdq6_6N2!Y)=9CKeS;v7z+QP176kY}qLaJglgmEfH? zPwLx2jMF)KfgFu0^}#)ZHlS>ABig?B>ra?;b^q@88aS%(Q%ml*Q>E-;=pOBh-V+RO*E~p+fLe`^1S8MTpza zj{Sf_N~~$8Z-_or8@IkCc`BiXQ8ig=LqaQ{QuFGI%cDDJH+sOu#vf%btglZuNgo_E zOb}+x<-awL_UPt|A5I#qsJ(R)iXBYPuL15lN&QeVI`IrZ!xIuNk;*lu{;5b7KRypc zvY)%At=5e}=@{I19Jofu~1x=ˎNmjiB&TZ#=$ZT#s;Ki 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 literal 0 HcmV?d00001 diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000000..08f88d13d5 --- /dev/null +++ b/pom.xml @@ -0,0 +1,187 @@ + + + 4.0.0 + + com.amazonaws + aws-athena-query-federation + pom + 1.0 + + 1.8 + 1.8 + 1.11.490 + 2019.47.1 + 1.2.0 + 1.7.28 + 1.10.19 + 4.11 + + + + Amazon Web Services + https://https://aws.amazon.com// + + + 2019 + + + + Apache License 2.0 + http://www.apache.org/licenses/LICENSE-2.0 + repo + + + + + scm:git@github.com:awslabs/aws-athena-query-federation.git + https://github.com/awslabs/aws-athena-query-federation + HEAD + + + + athena-cloudwatch + athena-docdb + athena-redis + athena-bigquery + athena-aws-cmdb + athena-android + athena-dynamodb + athena-hbase + athena-cloudwatch-metrics + athena-example + athena-federation-sdk + athena-tpcds + athena-jdbc + athena-federation-sdk-tools + athena-udfs + + + + + org.slf4j + slf4j-log4j12 + ${slf4j-log4j.version} + + + + com.amazonaws + aws-lambda-java-core + ${aws.lambda-java-core.version} + + + + junit + junit + ${junit.version} + test + + + org.mockito + mockito-all + ${mockito.version} + test + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.1 + + + package + + shade + + + + + classworlds:classworlds + junit:junit + jmock:* + *:xml-apis + org.apache.maven:lib:tests + java.* + + + + + + + + + org.jacoco + jacoco-maven-plugin + 0.8.4 + + + + prepare-agent + + + + + report + test + + report + + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + 3.1.0 + + checkstyle.xml + UTF-8 + true + true + false + + + + validate + validate + + check + + + + + + + org.codehaus.mojo + license-maven-plugin + 2.0.0 + + false + false + false + + + + first + + update-file-header + + process-sources + + apache_v2 + + src/main/java + src/test + + + + + + + + diff --git a/tools/prepare_dev_env.sh b/tools/prepare_dev_env.sh new file mode 100755 index 0000000000..55444d49af --- /dev/null +++ b/tools/prepare_dev_env.sh @@ -0,0 +1,74 @@ +#!/bin/bash + +# Copyright (C) 2019 Amazon Web Services +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cat << EOF +# +# This script will prepare your development environment by installing certain pre-requisites, namely: +# 1. Apache Maven +# 2. HomeBrew - a package manager that will be used to fetch the next two pre-requistes. +# 3. AWS CLI (latest version) +# 4. AWS SAM Build Tool (latest version) +# +# This script has been designed and tested to work on Amazon Linux but may require slight adjustment for other Operating Systems. +# All tools used here (except HomeBrew) are supported on all major Operating Systems: Windows, Linux, Mac OS. +# +# This script may prompt you for yes/no responses or permission to continue at verious points. It is not meant to run unattended. +# +EOF + +while true; do + read -p "Do you wish to proceed? (yes or no) " yn + case $yn in + [Yy]* ) echo "Proceeding..."; break;; + [Nn]* ) exit;; + * ) echo "Please answer yes or no.";; + esac +done + +set -e +sudo wget https://archive.apache.org/dist/maven/maven-3/3.5.4/binaries/apache-maven-3.5.4-bin.tar.gz -O /tmp/apache-maven-3.5.4-bin.tar.gz +sudo tar xf /tmp/apache-maven-3.5.4-bin.tar.gz -C /opt +echo "export M2_HOME=/opt/apache-maven-3.5.4" >> ~/.profile +echo "export PATH=\${M2_HOME}/bin:\${PATH}" >> ~/.profile +echo "export M2_HOME=/opt/apache-maven-3.5.4" >> ~/.bash_profile +echo "export PATH=\${M2_HOME}/bin:\${PATH}" >> ~/.bash_profile + +sudo yum -y install java-1.8.0-openjdk-devel +sudo update-alternatives --set java /usr/lib/jvm/jre-1.8.0-openjdk.x86_64/bin/java +sudo update-alternatives --set javac /usr/lib/jvm/java-1.8.0-openjdk.x86_64/bin/javac + +# If the above update-alternatives doesn't work and you don't know your path try +# sudo update-alternatives --config java +# sudo update-alternatives --config javac + +sh -c "$(curl -fsSL https://raw.githubusercontent.com/Linuxbrew/install/master/install.sh)" +test -d ~/.linuxbrew && eval $(~/.linuxbrew/bin/brew shellenv) +test -d /home/linuxbrew/.linuxbrew && eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv) +test -r ~/.bash_profile && echo "eval \$($(brew --prefix)/bin/brew shellenv)" >>~/.bash_profile +echo "eval \$($(brew --prefix)/bin/brew shellenv)" >>~/.profile +echo "eval \$($(brew --prefix)/bin/brew shellenv)" >>~/.bash_profile + +source ~/.profile + +brew tap aws/tap +brew reinstall awscli +brew reinstall aws-sam-cli + +aws --version +sam --version + +echo "" +echo "" +echo "To ensure your terminal can see the new tools we installed run `source ~/.profile` or open a fresh terminal." \ No newline at end of file diff --git a/tools/publish.sh b/tools/publish.sh new file mode 100755 index 0000000000..e1becba6dd --- /dev/null +++ b/tools/publish.sh @@ -0,0 +1,97 @@ +#!/bin/bash + +# Copyright (C) 2019 Amazon Web Services +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cat << EOF +# Run this script from the directory of the module (e.g. athena-example) that you wish to publish. +# This script performs the following actions: +# 1. Builds the maven project +# 2. Creates a Serverless Application Package using the athena-example.yaml +# 3. Produces a final packaged.yaml which can be used to publish the application to your +# private Serverless Application Repository or deployed via Cloudformation. +# 4. Uploads the packaged connector code to the S3 bucket you specified. +# 5. Uses sar_bucket_policy.json to grant Serverless Application Repository access to our connector code in s3. +# 6. Published the connector to you private Serverless Application Repository where you can 1-click deploy it. +EOF + +while true; do + read -p "Do you wish to proceed? (yes or no) " yn + case $yn in + [Yy]* ) echo "Proceeding..."; break;; + [Nn]* ) exit;; + * ) echo "Please answer yes or no.";; + esac +done + +if [ "$#" -lt 2 ]; then + echo "\n\nERROR: Script requires 3 arguments \n" + echo "\n1. S3_BUCKET used for publishing artifacts to Lambda/Serverless App Repo.\n" + echo "\n2. The connector module to publish (e.g. athena-exmaple or athena-cloudwatch) \n" + echo "\n3. The AS REGION to target (e.g. us-east-1 or us-east-2) \n" + echo "\n\n USAGE from the module's directory: ../tools/publish.sh my_s3_bucket athena-example \n" + exit; +fi + +if test -f "$2".yaml; then + echo "SAR yaml found. We appear to be in the right directory." +else + echo "SAR yaml not found, attempting to switch to module directory." + cd $2 +fi + +REGION=$3 +if [ -z "$REGION" ] +then + REGION="us-east-1" +fi + +echo "Using AWS Region $REGION" + + +if ! aws s3api get-bucket-policy --bucket $1 --region $REGION| grep 'Statement' ; then + echo "No bucket policy is set on $1 , would you like to add a Serverless Application Repository Bucket Policy?" + while true; do + read -p "Do you wish to proceed? (yes or no) " yn + case $yn in + [Yy]* ) echo "Proceeding..."; break;; + [Nn]* ) echo "Skipping bucket policy, not that this may result in failed attempts to publish to Serverless Application Repository"; break;; + * ) echo "Please answer yes or no.";; + esac + done + +cat > sar_bucket_policy.json <<- EOM +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": "serverlessrepo.amazonaws.com" + }, + "Action": "s3:GetObject", + "Resource": "arn:aws:s3:::$1/*" + } + ] + } +EOM + set -e + aws s3api put-bucket-policy --bucket $1 --region $REGION --policy file://sar_bucket_policy.json +fi + +set -e +mvn clean install + +sam package --template-file $2.yaml --output-template-file packaged.yaml --s3-bucket $1 --region $REGION +sam publish --template packaged.yaml --region $REGION + diff --git a/tools/validate_connector.sh b/tools/validate_connector.sh new file mode 100755 index 0000000000..654a6c3164 --- /dev/null +++ b/tools/validate_connector.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +# Copyright (C) 2019 Amazon Web Services +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cat << EOF +# Run this script from any directory: +# 1. Builds the maven project, if needed. +# 2. Simulates an Athena query running against your connector that is deployed as a Lambda function. +# +# NOTE: That this test may cause a full table scan against your data source. If prompted to provide a +# query predicate, doing so will avoid a full table scan. You can also opt to stop the simulated query +# after the 'planning phase' so that it does not simulate process any splits. +# +# Use the -h or --help args to print usage information. +# +# Use 'yes | tools/validate_connector.sh [args]' to bypass this check. USE CAUTION +# +EOF + +while true; do + read -p "Do you wish to proceed? (yes or no) " yn + case $yn in + [Yy]* ) echo "Proceeding..."; break;; + [Nn]* ) exit;; + * ) echo "Please answer yes or no.";; + esac +done + +dir=$(cd -P -- "$(dirname -- "$0")" && pwd -P) + +cd "$dir/../athena-federation-sdk-tools" + +if test -f "target/athena-federation-sdk-tools-1.0.jar"; then + echo "athena-federation-sdk-tools is already built, skipping compilation." +else + mvn clean install +fi + +java -cp target/athena-federation-sdk-tools-1.0-withdep.jar com.amazonaws.athena.connector.validation.ConnectorValidator $@ \ No newline at end of file