From a36d549ccf22cb0cb5574553c03be3383aef2bf6 Mon Sep 17 00:00:00 2001 From: Daniel Harvey Date: Tue, 7 Nov 2023 17:12:11 +0000 Subject: [PATCH 1/7] WIP --- crates/ndc-sqlserver/src/configuration.rs | 63 +- crates/ndc-sqlserver/src/connector.rs | 8 +- .../ndc-sqlserver/src/table_configuration.sql | 60 + crates/ndc-sqlserver/tests/schema_tests.rs | 2 - .../snapshots/schema_tests__get_schema.snap | 1066 +---------------- justfile | 80 +- 6 files changed, 148 insertions(+), 1131 deletions(-) create mode 100644 crates/ndc-sqlserver/src/table_configuration.sql diff --git a/crates/ndc-sqlserver/src/configuration.rs b/crates/ndc-sqlserver/src/configuration.rs index 82022468..725f5f34 100644 --- a/crates/ndc-sqlserver/src/configuration.rs +++ b/crates/ndc-sqlserver/src/configuration.rs @@ -5,6 +5,9 @@ use ndc_sdk::connector; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use thiserror::Error; +use tiberius::Query; + +const TABLE_CONFIGURATION_QUERY: &str = include_str!("table_configuration.sql"); const CURRENT_VERSION: u32 = 1; @@ -94,17 +97,71 @@ async fn create_mssql_pool( /// Construct the deployment configuration by introspecting the database. pub async fn configure( - args: &RawConfiguration, + configuration: &RawConfiguration, ) -> Result { - // YOU WILL NOTICE NOTHING HAPPENS HERE, WE NEED TO INSPECT THE DATABASE PLEASE + let mssql_pool = create_mssql_pool(configuration).await.unwrap(); + + let mut connection = mssql_pool.get().await.unwrap(); + + // let's do a query to check everything is ok + let select = Query::new(TABLE_CONFIGURATION_QUERY); + + // go! + let stream = select.query(&mut connection).await.unwrap(); + + // Nothing is fetched, the first result set starts. + let row = stream.into_row().await.unwrap().unwrap(); + + let inner_result: Vec<&str> = vec![row.get(0).unwrap()]; + println!(); + + println!("{:?}", inner_result); + println!(); Ok(RawConfiguration { version: 1, - mssql_connection_string: args.mssql_connection_string.clone(), + mssql_connection_string: configuration.mssql_connection_string.clone(), metadata: query_engine_metadata::metadata::Metadata::default(), }) } +struct IntrospectionTable { + name: String, + schema_id: i32, + type_desc: String, + joined_sys_schema: IntrospectionSchema, + joined_sys_column: Vec, + joined_sys_primary_key: IntrospectionPrimaryKey, +} + +struct IntrospectionColumn { + name: String, + column_id: i32, + is_nullable: bool, + is_identity: bool, + is_computed: bool, + user_type_id: i32, + joined_sys_type: IntrospectionType, + joined_foreign_key_columns: Vec, +} + +struct IntrospectionForeignKeyColumn { + constraint_object_id: i32, + constraint_column_id: i32, + parent_object_id: i32, + parent_column_id: i32, + referenced_object_id: i32, + referenced_column_id: i32, + joined_referenced_table_name: String, + joined_referenced_column_name: String, + joined_referenced_sys_schema: IntrospectionSchema, +} + +struct IntrospectionSchema { + name: String, + schema_id: i32, +} + /// State initialization error. #[derive(Debug, Error)] pub enum InitializationError { diff --git a/crates/ndc-sqlserver/src/connector.rs b/crates/ndc-sqlserver/src/connector.rs index 6d5eacd4..0807c37b 100644 --- a/crates/ndc-sqlserver/src/connector.rs +++ b/crates/ndc-sqlserver/src/connector.rs @@ -118,7 +118,13 @@ impl connector::Connector for SQLServer { async fn get_schema( _configuration: &Self::Configuration, ) -> Result, connector::SchemaError> { - todo!("get_schema") + Ok(JsonResponse::Value(models::SchemaResponse { + scalar_types: Default::default(), + collections: Default::default(), + functions: Default::default(), + object_types: Default::default(), + procedures: Default::default(), + })) } /// Explain a query by creating an execution plan diff --git a/crates/ndc-sqlserver/src/table_configuration.sql b/crates/ndc-sqlserver/src/table_configuration.sql new file mode 100644 index 00000000..6894d364 --- /dev/null +++ b/crates/ndc-sqlserver/src/table_configuration.sql @@ -0,0 +1,60 @@ +-- SCHEMA_NAME(..) +SELECT ISNULL( + (SELECT object.name, object.schema_id, object.object_id, object.type_desc, + JSON_QUERY([schema].json) AS [joined_sys_schema], + JSON_QUERY([column].json) AS [joined_sys_column], + JSON_QUERY([primary_key].json) AS [joined_sys_primary_key] + FROM sys.objects object + CROSS APPLY (SELECT [column].name, [column].column_id, [column].is_nullable, [column].is_identity, [column].is_computed, [column].user_type_id, + JSON_QUERY([types].json) AS [joined_sys_type], + JSON_QUERY(ISNULL([relationships].json,'[]')) AS [joined_foreign_key_columns] + FROM sys.columns [column] + CROSS APPLY (SELECT name, schema_id, user_type_id FROM sys.types [type] + WHERE [type].user_type_id = [column].user_type_id + FOR JSON PATH, WITHOUT_ARRAY_WRAPPER) + AS [types](json) + CROSS APPLY (SELECT fk.*, + referenced_table.name AS joined_referenced_table_name, + referenced_column.name AS joined_referenced_column_name, + JSON_QUERY([schema].json) AS [joined_referenced_sys_schema] + FROM sys.foreign_key_columns AS [fk], + sys.columns AS referenced_column, + sys.objects AS referenced_table + CROSS APPLY (SELECT [schema].name, [schema].schema_id + FROM sys.schemas [schema] + WHERE [schema].schema_id = [referenced_table].schema_id + FOR JSON PATH, WITHOUT_ARRAY_WRAPPER) + AS [schema](json) + WHERE [object].object_id = fk.parent_object_id + AND [referenced_table].object_id = fk.referenced_object_id + AND [referenced_column].object_id = [referenced_table].object_id + AND [referenced_column].column_id = fk.referenced_column_id + AND [column].column_id = fk.parent_column_id + FOR JSON PATH) + AS [relationships](json) + WHERE [column].object_id = object.object_id + FOR JSON PATH) + AS [column](json) + CROSS APPLY (SELECT [schema].name, [schema].schema_id + FROM sys.schemas [schema] + WHERE [schema].schema_id = object.schema_id + FOR JSON PATH, WITHOUT_ARRAY_WRAPPER) + AS [schema](json) + CROSS APPLY (SELECT pk.name, pk.index_id, JSON_QUERY([cols].json) AS columns + FROM sys.indexes pk + CROSS APPLY (SELECT col.name + FROM sys.index_columns ic + INNER JOIN sys.columns col + ON col.column_id = ic.column_id + AND col.object_id = ic.object_id + WHERE ic.object_id = pk.object_id + AND ic.index_id = pk.index_id + FOR JSON PATH) + AS [cols](json) + WHERE pk.object_id = object.object_id + AND pk.is_primary_key = 1 + FOR JSON PATH, WITHOUT_ARRAY_WRAPPER) + AS [primary_key](json) + WHERE object.type_desc IN ('USER_TABLE', 'VIEW') + FOR JSON PATH) + , '[]') diff --git a/crates/ndc-sqlserver/tests/schema_tests.rs b/crates/ndc-sqlserver/tests/schema_tests.rs index 26fe5d0b..3684d8a1 100644 --- a/crates/ndc-sqlserver/tests/schema_tests.rs +++ b/crates/ndc-sqlserver/tests/schema_tests.rs @@ -1,4 +1,3 @@ -/* pub mod common; #[tokio::test] @@ -6,4 +5,3 @@ async fn get_schema() { let result = common::get_schema().await; insta::assert_json_snapshot!(result); } -*/ diff --git a/crates/ndc-sqlserver/tests/snapshots/schema_tests__get_schema.snap b/crates/ndc-sqlserver/tests/snapshots/schema_tests__get_schema.snap index abb914f4..ff9a101e 100644 --- a/crates/ndc-sqlserver/tests/snapshots/schema_tests__get_schema.snap +++ b/crates/ndc-sqlserver/tests/snapshots/schema_tests__get_schema.snap @@ -1,1071 +1,11 @@ --- source: crates/ndc-sqlserver/tests/schema_tests.rs -assertion_line: 6 expression: result --- { - "scalar_types": { - "Int": { - "aggregate_functions": { - "avg": { - "result_type": { - "type": "named", - "name": "Int" - } - }, - "bit_and": { - "result_type": { - "type": "named", - "name": "Int" - } - }, - "bit_or": { - "result_type": { - "type": "named", - "name": "Int" - } - }, - "bit_xor": { - "result_type": { - "type": "named", - "name": "Int" - } - }, - "count": { - "result_type": { - "type": "named", - "name": "Int" - } - }, - "dense_rank": { - "result_type": { - "type": "named", - "name": "Int" - } - }, - "max": { - "result_type": { - "type": "named", - "name": "Int" - } - }, - "min": { - "result_type": { - "type": "named", - "name": "Int" - } - }, - "ntile": { - "result_type": { - "type": "named", - "name": "Int" - } - }, - "rank": { - "result_type": { - "type": "named", - "name": "Int" - } - }, - "regr_count": { - "result_type": { - "type": "named", - "name": "Int" - } - }, - "row_number": { - "result_type": { - "type": "named", - "name": "Int" - } - }, - "st_clusterdbscan": { - "result_type": { - "type": "named", - "name": "Int" - } - }, - "st_clusterkmeans": { - "result_type": { - "type": "named", - "name": "Int" - } - }, - "stddev": { - "result_type": { - "type": "named", - "name": "Int" - } - }, - "stddev_pop": { - "result_type": { - "type": "named", - "name": "Int" - } - }, - "stddev_samp": { - "result_type": { - "type": "named", - "name": "Int" - } - }, - "sum": { - "result_type": { - "type": "named", - "name": "Int" - } - }, - "var_pop": { - "result_type": { - "type": "named", - "name": "Int" - } - }, - "var_samp": { - "result_type": { - "type": "named", - "name": "Int" - } - }, - "variance": { - "result_type": { - "type": "named", - "name": "Int" - } - } - }, - "comparison_operators": {}, - "update_operators": {} - }, - "String": { - "aggregate_functions": { - "max": { - "result_type": { - "type": "named", - "name": "String" - } - }, - "min": { - "result_type": { - "type": "named", - "name": "String" - } - }, - "string_agg": { - "result_type": { - "type": "named", - "name": "String" - } - } - }, - "comparison_operators": {}, - "update_operators": {} - }, - "any": { - "aggregate_functions": {}, - "comparison_operators": {}, - "update_operators": {} - } - }, - "object_types": { - "Album": { - "fields": { - "AlbumId": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "ArtistId": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "Title": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - } - } - }, - "Artist": { - "fields": { - "ArtistId": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "Name": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - } - } - }, - "Customer": { - "fields": { - "Address": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "City": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "Company": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "Country": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "CustomerId": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "Email": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "Fax": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "FirstName": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "LastName": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "Phone": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "PostalCode": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "State": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "SupportRepId": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - } - } - }, - "Employee": { - "fields": { - "Address": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "BirthDate": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "City": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "Country": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "Email": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "EmployeeId": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "Fax": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "FirstName": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "HireDate": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "LastName": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "Phone": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "PostalCode": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "ReportsTo": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "State": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "Title": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - } - } - }, - "Genre": { - "fields": { - "GenreId": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "Name": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - } - } - }, - "Invoice": { - "fields": { - "BillingAddress": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "BillingCity": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "BillingCountry": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "BillingPostalCode": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "BillingState": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "CustomerId": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "InvoiceDate": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "InvoiceId": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "Total": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - } - } - }, - "InvoiceLine": { - "fields": { - "InvoiceId": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "InvoiceLineId": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "Quantity": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "TrackId": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "UnitPrice": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - } - } - }, - "MediaType": { - "fields": { - "MediaTypeId": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "Name": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - } - } - }, - "Playlist": { - "fields": { - "Name": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "PlaylistId": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - } - } - }, - "PlaylistTrack": { - "fields": { - "PlaylistId": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "TrackId": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - } - } - }, - "Track": { - "fields": { - "AlbumId": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "Bytes": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "Composer": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "GenreId": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "MediaTypeId": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "Milliseconds": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "Name": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "TrackId": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "UnitPrice": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - } - } - }, - "geography_columns": { - "fields": { - "coord_dimension": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "f_geography_column": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "f_table_catalog": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "f_table_name": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "f_table_schema": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "srid": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "type": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - } - } - }, - "geometry_columns": { - "fields": { - "coord_dimension": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "f_geometry_column": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "f_table_catalog": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "f_table_name": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "f_table_schema": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "srid": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "type": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - } - } - }, - "spatial_ref_sys": { - "fields": { - "auth_name": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "auth_srid": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "proj4text": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "srid": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - }, - "srtext": { - "arguments": {}, - "type": { - "type": "named", - "name": "any" - } - } - } - } - }, - "collections": [ - { - "name": "Album", - "arguments": {}, - "type": "Album", - "deletable": false, - "uniqueness_constraints": { - "PK_Album": { - "unique_columns": [ - "AlbumId" - ] - } - }, - "foreign_keys": { - "FK_AlbumArtistId": { - "column_mapping": { - "ArtistId": "ArtistId" - }, - "foreign_collection": "Album" - } - } - }, - { - "name": "Artist", - "arguments": {}, - "type": "Artist", - "deletable": false, - "uniqueness_constraints": { - "PK_Artist": { - "unique_columns": [ - "ArtistId" - ] - } - }, - "foreign_keys": {} - }, - { - "name": "Customer", - "arguments": {}, - "type": "Customer", - "deletable": false, - "uniqueness_constraints": { - "PK_Customer": { - "unique_columns": [ - "CustomerId" - ] - } - }, - "foreign_keys": { - "FK_CustomerSupportRepId": { - "column_mapping": { - "SupportRepId": "EmployeeId" - }, - "foreign_collection": "Customer" - } - } - }, - { - "name": "Employee", - "arguments": {}, - "type": "Employee", - "deletable": false, - "uniqueness_constraints": { - "PK_Employee": { - "unique_columns": [ - "EmployeeId" - ] - } - }, - "foreign_keys": { - "FK_EmployeeReportsTo": { - "column_mapping": { - "ReportsTo": "EmployeeId" - }, - "foreign_collection": "Employee" - } - } - }, - { - "name": "Genre", - "arguments": {}, - "type": "Genre", - "deletable": false, - "uniqueness_constraints": { - "PK_Genre": { - "unique_columns": [ - "GenreId" - ] - } - }, - "foreign_keys": {} - }, - { - "name": "Invoice", - "arguments": {}, - "type": "Invoice", - "deletable": false, - "uniqueness_constraints": { - "PK_Invoice": { - "unique_columns": [ - "InvoiceId" - ] - } - }, - "foreign_keys": { - "FK_InvoiceCustomerId": { - "column_mapping": { - "CustomerId": "CustomerId" - }, - "foreign_collection": "Invoice" - } - } - }, - { - "name": "InvoiceLine", - "arguments": {}, - "type": "InvoiceLine", - "deletable": false, - "uniqueness_constraints": { - "PK_InvoiceLine": { - "unique_columns": [ - "InvoiceLineId" - ] - } - }, - "foreign_keys": { - "FK_InvoiceLineInvoiceId": { - "column_mapping": { - "InvoiceId": "InvoiceId" - }, - "foreign_collection": "InvoiceLine" - }, - "FK_InvoiceLineTrackId": { - "column_mapping": { - "TrackId": "TrackId" - }, - "foreign_collection": "InvoiceLine" - } - } - }, - { - "name": "MediaType", - "arguments": {}, - "type": "MediaType", - "deletable": false, - "uniqueness_constraints": { - "PK_MediaType": { - "unique_columns": [ - "MediaTypeId" - ] - } - }, - "foreign_keys": {} - }, - { - "name": "Playlist", - "arguments": {}, - "type": "Playlist", - "deletable": false, - "uniqueness_constraints": { - "PK_Playlist": { - "unique_columns": [ - "PlaylistId" - ] - } - }, - "foreign_keys": {} - }, - { - "name": "PlaylistTrack", - "arguments": {}, - "type": "PlaylistTrack", - "deletable": false, - "uniqueness_constraints": { - "PK_PlaylistTrack": { - "unique_columns": [ - "PlaylistId", - "TrackId" - ] - } - }, - "foreign_keys": { - "FK_PlaylistTrackPlaylistId": { - "column_mapping": { - "PlaylistId": "PlaylistId" - }, - "foreign_collection": "PlaylistTrack" - }, - "FK_PlaylistTrackTrackId": { - "column_mapping": { - "TrackId": "TrackId" - }, - "foreign_collection": "PlaylistTrack" - } - } - }, - { - "name": "Track", - "arguments": {}, - "type": "Track", - "deletable": false, - "uniqueness_constraints": { - "PK_Track": { - "unique_columns": [ - "TrackId" - ] - } - }, - "foreign_keys": { - "FK_TrackAlbumId": { - "column_mapping": { - "AlbumId": "AlbumId" - }, - "foreign_collection": "Track" - }, - "FK_TrackGenreId": { - "column_mapping": { - "GenreId": "GenreId" - }, - "foreign_collection": "Track" - }, - "FK_TrackMediaTypeId": { - "column_mapping": { - "MediaTypeId": "MediaTypeId" - }, - "foreign_collection": "Track" - } - } - }, - { - "name": "geography_columns", - "arguments": {}, - "type": "geography_columns", - "deletable": false, - "uniqueness_constraints": {}, - "foreign_keys": {} - }, - { - "name": "geometry_columns", - "arguments": {}, - "type": "geometry_columns", - "deletable": false, - "uniqueness_constraints": {}, - "foreign_keys": {} - }, - { - "name": "spatial_ref_sys", - "arguments": {}, - "type": "spatial_ref_sys", - "deletable": false, - "uniqueness_constraints": { - "spatial_ref_sys_pkey": { - "unique_columns": [ - "srid" - ] - } - }, - "foreign_keys": {} - } - ], + "scalar_types": {}, + "object_types": {}, + "collections": [], "functions": [], "procedures": [] } diff --git a/justfile b/justfile index 4386f50e..388ee532 100644 --- a/justfile +++ b/justfile @@ -3,9 +3,8 @@ set shell := ["bash", "-c"] CONNECTOR_IMAGE_NAME := "ghcr.io/hasura/sqlserver-agent-rs" CONNECTOR_IMAGE_TAG := "dev" CONNECTOR_IMAGE := CONNECTOR_IMAGE_NAME + ":" + CONNECTOR_IMAGE_TAG -POSTGRESQL_CONNECTION_STRING := "sqlserverql://sqlserver:password@localhost:64002" CHINOOK_DEPLOYMENT := "static/chinook-deployment.json" - +SQLSERVER_CONNECTION_STRING := "DRIVER={ODBC Driver 18 for SQL Server};SERVER=127.0.0.1,64003;Uid=SA;Database=Chinook;Pwd=Password!" # check everything check: format-check find-unused-dependencies build lint test @@ -21,47 +20,6 @@ run: start-dependencies RUST_LOG=INFO \ cargo run --release -- serve --configuration {{CHINOOK_DEPLOYMENT}} -# run the connector inside a Docker image -run-in-docker: build-docker-with-nix start-dependencies - #!/usr/bin/env bash - set -e -u -o pipefail - - configuration_file="$(mktemp)" - trap 'rm -f "$configuration_file"' EXIT - - echo '> Generating the configuration...' - docker run \ - --name=sqlserver-ndc-configuration \ - --rm \ - --detach \ - --platform=linux/amd64 \ - --net='sqlserver-ndc_default' \ - --publish='9100:9100' \ - {{CONNECTOR_IMAGE}} \ - configuration serve - trap 'docker stop sqlserver-ndc-configuration' EXIT - CONFIGURATION_SERVER_URL='http://localhost:9100/' - ./scripts/wait-until --timeout=30 --report -- nc -z localhost 9100 - curl -fsS "$CONFIGURATION_SERVER_URL" \ - | jq --arg sqlserver_database_url 'sqlserverql://sqlserver:password@sqlserver' '. + {"sqlserver_database_url": $sqlserver_database_url}' \ - | curl -fsS "$CONFIGURATION_SERVER_URL" -H 'Content-Type: application/json' -d @- \ - > "$configuration_file" - - echo '> Starting the server...' - docker run \ - --name=sqlserver-ndc \ - --rm \ - --interactive \ - --tty \ - --platform=linux/amd64 \ - --net='sqlserver-ndc_default' \ - --publish='8100:8100' \ - --env=RUST_LOG='INFO' \ - --mount="type=bind,source=${configuration_file},target=/deployment.json,readonly=true" \ - {{CONNECTOR_IMAGE}} \ - serve \ - --configuration='/deployment.json' - # watch the code, then test and re-run on changes dev: start-dependencies RUST_LOG=INFO \ @@ -71,6 +29,23 @@ dev: start-dependencies -x clippy \ -x 'run -- serve --configuration {{CHINOOK_DEPLOYMENT}}' +# watch the code, then test and re-run config server ron changes +dev-config: start-dependencies + RUST_LOG=DEBUG \ + cargo watch -i "tests/snapshots/*" \ + -c \ + -x test \ + -x clippy \ + -x 'run -- configuration serve' + +test-introspection: + #!/bin/bash + + CONFIGURATION_SERVER_URL='http://localhost:9100/' + curl -fsS "$CONFIGURATION_SERVER_URL" \ + | jq --arg sqlserver_database_url '{{ SQLSERVER_CONNECTION_STRING }}' '. + {"mssql_connection_string": $sqlserver_database_url}' \ + | curl -fsS "$CONFIGURATION_SERVER_URL" -H 'Content-Type: application/json' -d @- \ + # watch the code, and re-run on changes watch-run: start-dependencies RUST_LOG=DEBUG \ @@ -107,25 +82,6 @@ test-integrated: http://localhost:3000/graphql \ -d '{ "query": "query { AlbumByID(AlbumId: 1) { Title } } " }' -# re-generate the deployment configuration file -generate-chinook-configuration: build - #!/usr/bin/env bash - set -e -u - - cargo run --quiet -- configuration serve & - CONFIGURATION_SERVER_PID=$! - trap "kill $CONFIGURATION_SERVER_PID" EXIT - ./scripts/wait-until --timeout=30 --report -- nc -z localhost 9100 - if ! kill -0 "$CONFIGURATION_SERVER_PID"; then - echo >&2 'The server stopped abruptly.' - exit 1 - fi - curl -fsS http://localhost:9100 \ - | jq --arg sqlserver_database_url '{{POSTGRESQL_CONNECTION_STRING}}' '. + {"sqlserver_database_url": $sqlserver_database_url}' \ - | curl -fsS http://localhost:9100 -H 'Content-Type: application/json' -d @- \ - | jq . \ - > '{{CHINOOK_DEPLOYMENT}}' - # run sqlserver start-dependencies: # start sqlserver From 336083a005a4f9234f83486b680546ee212ecb8d Mon Sep 17 00:00:00 2001 From: Daniel Harvey Date: Wed, 8 Nov 2023 16:04:41 +0000 Subject: [PATCH 2/7] Shuffle files around --- .../src/configuration/introspection.rs | 63 +++++++++++++++++++ crates/ndc-sqlserver/src/configuration/mod.rs | 7 +++ .../table_configuration.sql | 0 .../version1.rs} | 46 ++------------ 4 files changed, 76 insertions(+), 40 deletions(-) create mode 100644 crates/ndc-sqlserver/src/configuration/introspection.rs create mode 100644 crates/ndc-sqlserver/src/configuration/mod.rs rename crates/ndc-sqlserver/src/{ => configuration}/table_configuration.sql (100%) rename crates/ndc-sqlserver/src/{configuration.rs => configuration/version1.rs} (79%) diff --git a/crates/ndc-sqlserver/src/configuration/introspection.rs b/crates/ndc-sqlserver/src/configuration/introspection.rs new file mode 100644 index 00000000..0eacac4c --- /dev/null +++ b/crates/ndc-sqlserver/src/configuration/introspection.rs @@ -0,0 +1,63 @@ +//! Configuration and state for our connector. + +use serde::Deserialize; + +#[derive(Deserialize, Debug)] +pub struct IntrospectionTable { + name: String, + schema_id: i32, + type_desc: String, + joined_sys_schema: IntrospectionSchema, + joined_sys_column: Vec, + joined_sys_primary_key: IntrospectionPrimaryKey, +} + +#[derive(Deserialize, Debug)] +pub struct IntrospectionColumn { + name: String, + column_id: i32, + is_nullable: bool, + is_identity: bool, + is_computed: bool, + user_type_id: i32, + joined_sys_type: IntrospectionType, + joined_foreign_key_columns: Vec, +} + +#[derive(Deserialize, Debug)] +pub struct IntrospectionType { + name: String, + schema_id: i32, + user_type_id: i32, +} + +#[derive(Deserialize, Debug)] +pub struct IntrospectionPrimaryKey { + name: String, + index_id: i32, + columns: Vec, +} + +#[derive(Deserialize, Debug)] +pub struct IntrospectionPrimaryKeyColumn { + name: String, +} + +#[derive(Deserialize, Debug)] +pub struct IntrospectionForeignKeyColumn { + constraint_object_id: i32, + constraint_column_id: i32, + parent_object_id: i32, + parent_column_id: i32, + referenced_object_id: i32, + referenced_column_id: i32, + joined_referenced_table_name: String, + joined_referenced_column_name: String, + joined_referenced_sys_schema: IntrospectionSchema, +} + +#[derive(Deserialize, Debug)] +pub struct IntrospectionSchema { + name: String, + schema_id: i32, +} diff --git a/crates/ndc-sqlserver/src/configuration/mod.rs b/crates/ndc-sqlserver/src/configuration/mod.rs new file mode 100644 index 00000000..8c1d86c0 --- /dev/null +++ b/crates/ndc-sqlserver/src/configuration/mod.rs @@ -0,0 +1,7 @@ +pub mod introspection; +pub mod version1; + +pub use version1::{ + configure, create_state, validate_raw_configuration, Configuration, InitializationError, + RawConfiguration, State, +}; diff --git a/crates/ndc-sqlserver/src/table_configuration.sql b/crates/ndc-sqlserver/src/configuration/table_configuration.sql similarity index 100% rename from crates/ndc-sqlserver/src/table_configuration.sql rename to crates/ndc-sqlserver/src/configuration/table_configuration.sql diff --git a/crates/ndc-sqlserver/src/configuration.rs b/crates/ndc-sqlserver/src/configuration/version1.rs similarity index 79% rename from crates/ndc-sqlserver/src/configuration.rs rename to crates/ndc-sqlserver/src/configuration/version1.rs index 725f5f34..0fe7df7e 100644 --- a/crates/ndc-sqlserver/src/configuration.rs +++ b/crates/ndc-sqlserver/src/configuration/version1.rs @@ -1,6 +1,7 @@ //! Configuration and state for our connector. -use super::metrics; +use crate::metrics; +use crate::configuration::introspection; use ndc_sdk::connector; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -112,10 +113,12 @@ pub async fn configure( // Nothing is fetched, the first result set starts. let row = stream.into_row().await.unwrap().unwrap(); - let inner_result: Vec<&str> = vec![row.get(0).unwrap()]; + let inner_result = row.get(0).unwrap(); println!(); + let decoded: Vec = + serde_json::from_str(inner_result).unwrap(); - println!("{:?}", inner_result); + println!("{:?}", decoded); println!(); Ok(RawConfiguration { @@ -125,43 +128,6 @@ pub async fn configure( }) } -struct IntrospectionTable { - name: String, - schema_id: i32, - type_desc: String, - joined_sys_schema: IntrospectionSchema, - joined_sys_column: Vec, - joined_sys_primary_key: IntrospectionPrimaryKey, -} - -struct IntrospectionColumn { - name: String, - column_id: i32, - is_nullable: bool, - is_identity: bool, - is_computed: bool, - user_type_id: i32, - joined_sys_type: IntrospectionType, - joined_foreign_key_columns: Vec, -} - -struct IntrospectionForeignKeyColumn { - constraint_object_id: i32, - constraint_column_id: i32, - parent_object_id: i32, - parent_column_id: i32, - referenced_object_id: i32, - referenced_column_id: i32, - joined_referenced_table_name: String, - joined_referenced_column_name: String, - joined_referenced_sys_schema: IntrospectionSchema, -} - -struct IntrospectionSchema { - name: String, - schema_id: i32, -} - /// State initialization error. #[derive(Debug, Error)] pub enum InitializationError { From ac74981d98f0eea407863a717d2062d1c754aa11 Mon Sep 17 00:00:00 2001 From: Daniel Harvey Date: Thu, 9 Nov 2023 12:07:37 +0000 Subject: [PATCH 3/7] Not bad --- .../src/configuration/introspection.rs | 27 +++---- .../src/configuration/version1.rs | 81 ++++++++++++++++++- 2 files changed, 91 insertions(+), 17 deletions(-) diff --git a/crates/ndc-sqlserver/src/configuration/introspection.rs b/crates/ndc-sqlserver/src/configuration/introspection.rs index 0eacac4c..622e0d15 100644 --- a/crates/ndc-sqlserver/src/configuration/introspection.rs +++ b/crates/ndc-sqlserver/src/configuration/introspection.rs @@ -1,46 +1,45 @@ //! Configuration and state for our connector. - use serde::Deserialize; #[derive(Deserialize, Debug)] pub struct IntrospectionTable { - name: String, + pub name: String, schema_id: i32, - type_desc: String, - joined_sys_schema: IntrospectionSchema, - joined_sys_column: Vec, - joined_sys_primary_key: IntrospectionPrimaryKey, + pub type_desc: String, + pub joined_sys_schema: IntrospectionSchema, + pub joined_sys_column: Vec, + pub joined_sys_primary_key: Option, } #[derive(Deserialize, Debug)] pub struct IntrospectionColumn { - name: String, + pub name: String, column_id: i32, - is_nullable: bool, + pub is_nullable: bool, is_identity: bool, is_computed: bool, user_type_id: i32, - joined_sys_type: IntrospectionType, + pub joined_sys_type: IntrospectionType, joined_foreign_key_columns: Vec, } #[derive(Deserialize, Debug)] pub struct IntrospectionType { - name: String, + pub name: String, schema_id: i32, user_type_id: i32, } #[derive(Deserialize, Debug)] pub struct IntrospectionPrimaryKey { - name: String, + pub name: String, index_id: i32, - columns: Vec, + pub columns: Vec, } #[derive(Deserialize, Debug)] pub struct IntrospectionPrimaryKeyColumn { - name: String, + pub name: String, } #[derive(Deserialize, Debug)] @@ -58,6 +57,6 @@ pub struct IntrospectionForeignKeyColumn { #[derive(Deserialize, Debug)] pub struct IntrospectionSchema { - name: String, + pub name: String, schema_id: i32, } diff --git a/crates/ndc-sqlserver/src/configuration/version1.rs b/crates/ndc-sqlserver/src/configuration/version1.rs index 0fe7df7e..6286e47e 100644 --- a/crates/ndc-sqlserver/src/configuration/version1.rs +++ b/crates/ndc-sqlserver/src/configuration/version1.rs @@ -3,8 +3,11 @@ use crate::metrics; use crate::configuration::introspection; use ndc_sdk::connector; +use query_engine_metadata::metadata::{database, Nullable}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +use std::collections::BTreeMap; +use std::collections::BTreeSet; use thiserror::Error; use tiberius::Query; @@ -114,20 +117,92 @@ pub async fn configure( let row = stream.into_row().await.unwrap().unwrap(); let inner_result = row.get(0).unwrap(); - println!(); let decoded: Vec = serde_json::from_str(inner_result).unwrap(); println!("{:?}", decoded); - println!(); + + let mut metadata = query_engine_metadata::metadata::Metadata::default(); + + metadata.tables = get_tables_info(decoded); Ok(RawConfiguration { version: 1, mssql_connection_string: configuration.mssql_connection_string.clone(), - metadata: query_engine_metadata::metadata::Metadata::default(), + metadata, }) } +fn get_tables_info( + introspection_tables: Vec, +) -> database::TablesInfo { + let mut tables = BTreeMap::new(); + + for introspection_table in introspection_tables { + let table_name = introspection_table.name; + + let mut columns = BTreeMap::new(); + + for introspection_column in introspection_table.joined_sys_column { + let column_name = introspection_column.name.clone(); + columns.insert(column_name, get_column_info(introspection_column)); + } + + let table_info = database::TableInfo { + columns, + description: None, + foreign_relations: Default::default(), + table_name: table_name.clone(), + schema_name: introspection_table.joined_sys_schema.name, + uniqueness_constraints: get_uniqueness_constraints( + introspection_table.joined_sys_primary_key, + ), + }; + + tables.insert(table_name, table_info); + } + + database::TablesInfo(tables) +} + +fn get_uniqueness_constraints( + opt_primary_key: Option, +) -> database::UniquenessConstraints { + let mut uniqueness_constraints_inner = BTreeMap::new(); + + if let Some(primary_key) = opt_primary_key { + let constraint_name = primary_key.name; + + let keys_set = primary_key + .columns + .iter() + .fold(BTreeSet::new(), |mut set, part| { + set.insert(part.name.clone()); + set + }); + + uniqueness_constraints_inner + .insert(constraint_name, database::UniquenessConstraint(keys_set)); + } + + database::UniquenessConstraints(uniqueness_constraints_inner) +} + +fn get_column_info( + introspection_column: introspection::IntrospectionColumn, +) -> database::ColumnInfo { + database::ColumnInfo { + description: None, + name: introspection_column.name, + nullable: if introspection_column.is_nullable { + Nullable::Nullable + } else { + Nullable::NonNullable + }, + r#type: database::ScalarType(introspection_column.joined_sys_type.name), + } +} + /// State initialization error. #[derive(Debug, Error)] pub enum InitializationError { From 1b8a276fc79154802c52f3376403e1670fdd2b47 Mon Sep 17 00:00:00 2001 From: Daniel Harvey Date: Thu, 9 Nov 2023 16:43:57 +0000 Subject: [PATCH 4/7] Somewhat works --- .../src/configuration/introspection.rs | 6 +-- .../src/configuration/version1.rs | 46 ++++++++++++++++--- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/crates/ndc-sqlserver/src/configuration/introspection.rs b/crates/ndc-sqlserver/src/configuration/introspection.rs index 622e0d15..df986635 100644 --- a/crates/ndc-sqlserver/src/configuration/introspection.rs +++ b/crates/ndc-sqlserver/src/configuration/introspection.rs @@ -20,7 +20,7 @@ pub struct IntrospectionColumn { is_computed: bool, user_type_id: i32, pub joined_sys_type: IntrospectionType, - joined_foreign_key_columns: Vec, + pub joined_foreign_key_columns: Vec, } #[derive(Deserialize, Debug)] @@ -50,8 +50,8 @@ pub struct IntrospectionForeignKeyColumn { parent_column_id: i32, referenced_object_id: i32, referenced_column_id: i32, - joined_referenced_table_name: String, - joined_referenced_column_name: String, + pub joined_referenced_table_name: String, + pub joined_referenced_column_name: String, joined_referenced_sys_schema: IntrospectionSchema, } diff --git a/crates/ndc-sqlserver/src/configuration/version1.rs b/crates/ndc-sqlserver/src/configuration/version1.rs index 6286e47e..ec11dbaf 100644 --- a/crates/ndc-sqlserver/src/configuration/version1.rs +++ b/crates/ndc-sqlserver/src/configuration/version1.rs @@ -120,11 +120,10 @@ pub async fn configure( let decoded: Vec = serde_json::from_str(inner_result).unwrap(); - println!("{:?}", decoded); - let mut metadata = query_engine_metadata::metadata::Metadata::default(); metadata.tables = get_tables_info(decoded); + metadata.native_queries = configuration.metadata.native_queries.clone(); Ok(RawConfiguration { version: 1, @@ -142,16 +141,19 @@ fn get_tables_info( let table_name = introspection_table.name; let mut columns = BTreeMap::new(); + let mut foreign_relations_inner = BTreeMap::new(); for introspection_column in introspection_table.joined_sys_column { let column_name = introspection_column.name.clone(); - columns.insert(column_name, get_column_info(introspection_column)); + let (column, new_foreign_relations) = get_column_info(introspection_column); + columns.insert(column_name, column); + foreign_relations_inner.extend(new_foreign_relations); } let table_info = database::TableInfo { columns, description: None, - foreign_relations: Default::default(), + foreign_relations: database::ForeignRelations(foreign_relations_inner), table_name: table_name.clone(), schema_name: introspection_table.joined_sys_schema.name, uniqueness_constraints: get_uniqueness_constraints( @@ -165,6 +167,19 @@ fn get_tables_info( database::TablesInfo(tables) } +fn get_foreign_relation( + local_column: String, + foreign_key: introspection::IntrospectionForeignKeyColumn, +) -> database::ForeignRelation { + let mut column_mapping = BTreeMap::new(); + column_mapping.insert(local_column, foreign_key.joined_referenced_column_name); + + database::ForeignRelation { + foreign_table: foreign_key.joined_referenced_table_name, + column_mapping, + } +} + fn get_uniqueness_constraints( opt_primary_key: Option, ) -> database::UniquenessConstraints { @@ -190,8 +205,24 @@ fn get_uniqueness_constraints( fn get_column_info( introspection_column: introspection::IntrospectionColumn, -) -> database::ColumnInfo { - database::ColumnInfo { +) -> ( + database::ColumnInfo, + BTreeMap, +) { + let mut foreign_relations = BTreeMap::new(); + introspection_column + .joined_foreign_key_columns + .into_iter() + .enumerate() + .for_each(|(index, foreign_key)| { + let fancy_key = format!("FK_{}{}", introspection_column.name, index); + foreign_relations.insert( + fancy_key, + get_foreign_relation(introspection_column.name.clone(), foreign_key), + ); + }); + + let column_info = database::ColumnInfo { description: None, name: introspection_column.name, nullable: if introspection_column.is_nullable { @@ -200,7 +231,8 @@ fn get_column_info( Nullable::NonNullable }, r#type: database::ScalarType(introspection_column.joined_sys_type.name), - } + }; + (column_info, foreign_relations) } /// State initialization error. From 813976ecdd42b0ebf56fd04d5b725310cc236cb3 Mon Sep 17 00:00:00 2001 From: Daniel Harvey Date: Thu, 9 Nov 2023 16:50:57 +0000 Subject: [PATCH 5/7] Remove unused --- .../src/configuration/introspection.rs | 19 +++---------------- .../src/configuration/version1.rs | 4 ++-- 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/crates/ndc-sqlserver/src/configuration/introspection.rs b/crates/ndc-sqlserver/src/configuration/introspection.rs index df986635..993a1b70 100644 --- a/crates/ndc-sqlserver/src/configuration/introspection.rs +++ b/crates/ndc-sqlserver/src/configuration/introspection.rs @@ -4,7 +4,6 @@ use serde::Deserialize; #[derive(Deserialize, Debug)] pub struct IntrospectionTable { pub name: String, - schema_id: i32, pub type_desc: String, pub joined_sys_schema: IntrospectionSchema, pub joined_sys_column: Vec, @@ -14,11 +13,9 @@ pub struct IntrospectionTable { #[derive(Deserialize, Debug)] pub struct IntrospectionColumn { pub name: String, - column_id: i32, pub is_nullable: bool, - is_identity: bool, - is_computed: bool, - user_type_id: i32, + pub is_identity: bool, + pub is_computed: bool, pub joined_sys_type: IntrospectionType, pub joined_foreign_key_columns: Vec, } @@ -26,14 +23,11 @@ pub struct IntrospectionColumn { #[derive(Deserialize, Debug)] pub struct IntrospectionType { pub name: String, - schema_id: i32, - user_type_id: i32, } #[derive(Deserialize, Debug)] pub struct IntrospectionPrimaryKey { pub name: String, - index_id: i32, pub columns: Vec, } @@ -44,19 +38,12 @@ pub struct IntrospectionPrimaryKeyColumn { #[derive(Deserialize, Debug)] pub struct IntrospectionForeignKeyColumn { - constraint_object_id: i32, - constraint_column_id: i32, - parent_object_id: i32, - parent_column_id: i32, - referenced_object_id: i32, - referenced_column_id: i32, pub joined_referenced_table_name: String, pub joined_referenced_column_name: String, - joined_referenced_sys_schema: IntrospectionSchema, + pub joined_referenced_sys_schema: IntrospectionSchema, } #[derive(Deserialize, Debug)] pub struct IntrospectionSchema { pub name: String, - schema_id: i32, } diff --git a/crates/ndc-sqlserver/src/configuration/version1.rs b/crates/ndc-sqlserver/src/configuration/version1.rs index ec11dbaf..a16714a1 100644 --- a/crates/ndc-sqlserver/src/configuration/version1.rs +++ b/crates/ndc-sqlserver/src/configuration/version1.rs @@ -116,13 +116,13 @@ pub async fn configure( // Nothing is fetched, the first result set starts. let row = stream.into_row().await.unwrap().unwrap(); - let inner_result = row.get(0).unwrap(); let decoded: Vec = - serde_json::from_str(inner_result).unwrap(); + serde_json::from_str(row.get(0).unwrap()).unwrap(); let mut metadata = query_engine_metadata::metadata::Metadata::default(); metadata.tables = get_tables_info(decoded); + metadata.native_queries = configuration.metadata.native_queries.clone(); Ok(RawConfiguration { From d06cb91748eeaccddd2a207776a7991d87ba9259 Mon Sep 17 00:00:00 2001 From: Daniel Harvey Date: Thu, 9 Nov 2023 17:22:41 +0000 Subject: [PATCH 6/7] Steal ndc-postgres get_schema implementation --- crates/ndc-sqlserver/src/configuration/mod.rs | 4 +- .../src/configuration/version1.rs | 28 + crates/ndc-sqlserver/src/connector.rs | 11 +- crates/ndc-sqlserver/src/lib.rs | 1 + crates/ndc-sqlserver/src/schema.rs | 210 ++ .../snapshots/schema_tests__get_schema.snap | 2980 ++++++++++++++++- 6 files changed, 3221 insertions(+), 13 deletions(-) create mode 100644 crates/ndc-sqlserver/src/schema.rs diff --git a/crates/ndc-sqlserver/src/configuration/mod.rs b/crates/ndc-sqlserver/src/configuration/mod.rs index 8c1d86c0..44d3dd5b 100644 --- a/crates/ndc-sqlserver/src/configuration/mod.rs +++ b/crates/ndc-sqlserver/src/configuration/mod.rs @@ -2,6 +2,6 @@ pub mod introspection; pub mod version1; pub use version1::{ - configure, create_state, validate_raw_configuration, Configuration, InitializationError, - RawConfiguration, State, + configure, create_state, occurring_scalar_types, validate_raw_configuration, Configuration, + InitializationError, RawConfiguration, State, }; diff --git a/crates/ndc-sqlserver/src/configuration/version1.rs b/crates/ndc-sqlserver/src/configuration/version1.rs index a16714a1..80b98f2e 100644 --- a/crates/ndc-sqlserver/src/configuration/version1.rs +++ b/crates/ndc-sqlserver/src/configuration/version1.rs @@ -3,6 +3,7 @@ use crate::metrics; use crate::configuration::introspection; use ndc_sdk::connector; +use query_engine_metadata::metadata; use query_engine_metadata::metadata::{database, Nullable}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -243,3 +244,30 @@ pub enum InitializationError { #[error("error initializing Prometheus metrics: {0}")] PrometheusError(prometheus::Error), } + +/// Collect all the types that can occur in the metadata. This is a bit circumstantial. A better +/// approach is likely to record scalar type names directly in the metadata via configuration.sql. +pub fn occurring_scalar_types( + tables: &metadata::TablesInfo, + native_queries: &metadata::NativeQueries, +) -> BTreeSet { + let tables_column_types = tables + .0 + .values() + .flat_map(|v| v.columns.values().map(|c| c.r#type.clone())); + + let native_queries_column_types = native_queries + .0 + .values() + .flat_map(|v| v.columns.values().map(|c| c.r#type.clone())); + + let native_queries_arguments_types = native_queries + .0 + .values() + .flat_map(|v| v.arguments.values().map(|c| c.r#type.clone())); + + tables_column_types + .chain(native_queries_column_types) + .chain(native_queries_arguments_types) + .collect::>() +} diff --git a/crates/ndc-sqlserver/src/connector.rs b/crates/ndc-sqlserver/src/connector.rs index 0807c37b..6793123b 100644 --- a/crates/ndc-sqlserver/src/connector.rs +++ b/crates/ndc-sqlserver/src/connector.rs @@ -15,6 +15,7 @@ use query_engine_execution::execution; use query_engine_translation::translation; use super::configuration; +use super::schema; #[derive(Clone, Default)] pub struct SQLServer {} @@ -116,15 +117,9 @@ impl connector::Connector for SQLServer { /// This function implements the [schema endpoint](https://hasura.github.io/ndc-spec/specification/schema/index.html) /// from the NDC specification. async fn get_schema( - _configuration: &Self::Configuration, + configuration: &Self::Configuration, ) -> Result, connector::SchemaError> { - Ok(JsonResponse::Value(models::SchemaResponse { - scalar_types: Default::default(), - collections: Default::default(), - functions: Default::default(), - object_types: Default::default(), - procedures: Default::default(), - })) + schema::get_schema(configuration).await.map(Into::into) } /// Explain a query by creating an execution plan diff --git a/crates/ndc-sqlserver/src/lib.rs b/crates/ndc-sqlserver/src/lib.rs index 8f09365f..1d98c5e8 100644 --- a/crates/ndc-sqlserver/src/lib.rs +++ b/crates/ndc-sqlserver/src/lib.rs @@ -1,3 +1,4 @@ pub mod configuration; pub mod connector; pub mod metrics; +pub mod schema; diff --git a/crates/ndc-sqlserver/src/schema.rs b/crates/ndc-sqlserver/src/schema.rs new file mode 100644 index 00000000..3040382e --- /dev/null +++ b/crates/ndc-sqlserver/src/schema.rs @@ -0,0 +1,210 @@ +//! Implement the `/schema` endpoint to return the connector's schema. +//! See the Hasura +//! [Native Data Connector Specification](https://hasura.github.io/ndc-spec/specification/schema/index.html) +//! for further details. + +use std::collections::BTreeMap; + +use ndc_sdk::connector; +use ndc_sdk::models; +use query_engine_metadata::metadata; + +use super::configuration; + +/// Get the connector's schema. +/// +/// This function implements the [schema endpoint](https://hasura.github.io/ndc-spec/specification/schema/index.html) +/// from the NDC specification. +pub async fn get_schema( + configuration::Configuration { config, .. }: &configuration::Configuration, +) -> Result { + let configuration::RawConfiguration { metadata, .. } = config; + let scalar_types: BTreeMap = + configuration::occurring_scalar_types(&metadata.tables, &metadata.native_queries) + .iter() + .map(|scalar_type| { + ( + scalar_type.0.clone(), + models::ScalarType { + aggregate_functions: metadata + .aggregate_functions + .0 + .get(scalar_type) + .unwrap_or(&BTreeMap::new()) + .iter() + .map(|(function_name, function_definition)| { + ( + function_name.clone(), + models::AggregateFunctionDefinition { + result_type: models::Type::Named { + name: function_definition.return_type.0.clone(), + }, + }, + ) + }) + .collect(), + comparison_operators: metadata + .comparison_operators + .0 + .get(scalar_type) + .unwrap_or(&BTreeMap::new()) + .iter() + .map(|(op_name, op_def)| { + ( + op_name.clone(), + models::ComparisonOperatorDefinition { + argument_type: models::Type::Named { + name: op_def.argument_type.0.clone(), + }, + }, + ) + }) + .collect(), + update_operators: BTreeMap::new(), + }, + ) + }) + .collect(); + + let tables: Vec = metadata + .tables + .0 + .iter() + .map(|(table_name, table)| models::CollectionInfo { + name: table_name.clone(), + description: table.description.clone(), + arguments: BTreeMap::new(), + collection_type: table_name.clone(), + insertable_columns: None, + updatable_columns: None, + deletable: false, + uniqueness_constraints: table + .uniqueness_constraints + .0 + .iter() + .map( + |(constraint_name, metadata::UniquenessConstraint(constraint_columns))| { + ( + constraint_name.clone(), + models::UniquenessConstraint { + unique_columns: constraint_columns.iter().cloned().collect(), + }, + ) + }, + ) + .collect(), + foreign_keys: table + .foreign_relations + .0 + .iter() + .map( + |( + constraint_name, + metadata::ForeignRelation { + foreign_table, + column_mapping, + }, + )| { + ( + constraint_name.clone(), + models::ForeignKeyConstraint { + foreign_collection: foreign_table.clone(), + column_mapping: column_mapping.clone(), + }, + ) + }, + ) + .collect(), + }) + .collect(); + + let native_queries: Vec = metadata + .native_queries + .0 + .iter() + .map(|(name, info)| models::CollectionInfo { + name: name.clone(), + description: info.description.clone(), + arguments: info + .arguments + .iter() + .map(|(name, column_info)| { + ( + name.clone(), + models::ArgumentInfo { + description: column_info.description.clone(), + argument_type: column_to_type(column_info), + }, + ) + }) + .collect(), + collection_type: name.clone(), + insertable_columns: None, + updatable_columns: None, + deletable: false, + uniqueness_constraints: BTreeMap::new(), + foreign_keys: BTreeMap::new(), + }) + .collect(); + + let mut collections = tables; + collections.extend(native_queries); + + let table_types = BTreeMap::from_iter(metadata.tables.0.iter().map(|(table_name, table)| { + let object_type = models::ObjectType { + description: table.description.clone(), + fields: BTreeMap::from_iter(table.columns.values().map(|column| { + ( + column.name.clone(), + models::ObjectField { + description: column.description.clone(), + r#type: column_to_type(column), + }, + ) + })), + }; + (table_name.clone(), object_type) + })); + + let native_queries_types = + BTreeMap::from_iter(metadata.native_queries.0.iter().map(|(name, info)| { + let object_type = models::ObjectType { + description: info.description.clone(), + fields: BTreeMap::from_iter(info.columns.values().map(|column| { + ( + column.name.clone(), + models::ObjectField { + description: column.description.clone(), + r#type: column_to_type(column), + }, + ) + })), + }; + (name.clone(), object_type) + })); + + let mut object_types = table_types; + object_types.extend(native_queries_types); + + Ok(models::SchemaResponse { + collections, + procedures: vec![], + functions: vec![], + object_types, + scalar_types, + }) +} + +fn column_to_type(column: &metadata::ColumnInfo) -> models::Type { + match &column.nullable { + metadata::Nullable::NonNullable => models::Type::Named { + name: column.r#type.0.clone(), + }, + metadata::Nullable::Nullable => models::Type::Nullable { + underlying_type: Box::new(models::Type::Named { + name: column.r#type.0.clone(), + }), + }, + } +} + diff --git a/crates/ndc-sqlserver/tests/snapshots/schema_tests__get_schema.snap b/crates/ndc-sqlserver/tests/snapshots/schema_tests__get_schema.snap index ff9a101e..c3a6cf59 100644 --- a/crates/ndc-sqlserver/tests/snapshots/schema_tests__get_schema.snap +++ b/crates/ndc-sqlserver/tests/snapshots/schema_tests__get_schema.snap @@ -3,9 +3,2983 @@ source: crates/ndc-sqlserver/tests/schema_tests.rs expression: result --- { - "scalar_types": {}, - "object_types": {}, - "collections": [], + "scalar_types": { + "bool": { + "aggregate_functions": { + "bool_and": { + "result_type": { + "type": "named", + "name": "bool" + } + }, + "bool_or": { + "result_type": { + "type": "named", + "name": "bool" + } + }, + "every": { + "result_type": { + "type": "named", + "name": "bool" + } + } + }, + "comparison_operators": { + "_eq": { + "argument_type": { + "type": "named", + "name": "bool" + } + }, + "_gt": { + "argument_type": { + "type": "named", + "name": "bool" + } + }, + "_gte": { + "argument_type": { + "type": "named", + "name": "bool" + } + }, + "_lt": { + "argument_type": { + "type": "named", + "name": "bool" + } + }, + "_lte": { + "argument_type": { + "type": "named", + "name": "bool" + } + }, + "_neq": { + "argument_type": { + "type": "named", + "name": "bool" + } + } + }, + "update_operators": {} + }, + "char": { + "aggregate_functions": {}, + "comparison_operators": { + "_eq": { + "argument_type": { + "type": "named", + "name": "char" + } + }, + "_gt": { + "argument_type": { + "type": "named", + "name": "char" + } + }, + "_gte": { + "argument_type": { + "type": "named", + "name": "char" + } + }, + "_ilike": { + "argument_type": { + "type": "named", + "name": "char" + } + }, + "_iregex": { + "argument_type": { + "type": "named", + "name": "char" + } + }, + "_like": { + "argument_type": { + "type": "named", + "name": "char" + } + }, + "_lt": { + "argument_type": { + "type": "named", + "name": "char" + } + }, + "_lte": { + "argument_type": { + "type": "named", + "name": "char" + } + }, + "_neq": { + "argument_type": { + "type": "named", + "name": "char" + } + }, + "_nilike": { + "argument_type": { + "type": "named", + "name": "char" + } + }, + "_niregex": { + "argument_type": { + "type": "named", + "name": "char" + } + }, + "_nlike": { + "argument_type": { + "type": "named", + "name": "char" + } + }, + "_nregex": { + "argument_type": { + "type": "named", + "name": "char" + } + }, + "_regex": { + "argument_type": { + "type": "named", + "name": "char" + } + } + }, + "update_operators": {} + }, + "date": { + "aggregate_functions": { + "max": { + "result_type": { + "type": "named", + "name": "date" + } + }, + "min": { + "result_type": { + "type": "named", + "name": "date" + } + } + }, + "comparison_operators": { + "_eq": { + "argument_type": { + "type": "named", + "name": "date" + } + }, + "_gt": { + "argument_type": { + "type": "named", + "name": "date" + } + }, + "_gte": { + "argument_type": { + "type": "named", + "name": "date" + } + }, + "_lt": { + "argument_type": { + "type": "named", + "name": "date" + } + }, + "_lte": { + "argument_type": { + "type": "named", + "name": "date" + } + }, + "_neq": { + "argument_type": { + "type": "named", + "name": "date" + } + } + }, + "update_operators": {} + }, + "float4": { + "aggregate_functions": { + "avg": { + "result_type": { + "type": "named", + "name": "float8" + } + }, + "max": { + "result_type": { + "type": "named", + "name": "float4" + } + }, + "min": { + "result_type": { + "type": "named", + "name": "float4" + } + }, + "stddev": { + "result_type": { + "type": "named", + "name": "float8" + } + }, + "stddev_pop": { + "result_type": { + "type": "named", + "name": "float8" + } + }, + "stddev_samp": { + "result_type": { + "type": "named", + "name": "float8" + } + }, + "sum": { + "result_type": { + "type": "named", + "name": "float4" + } + }, + "var_pop": { + "result_type": { + "type": "named", + "name": "float8" + } + }, + "var_samp": { + "result_type": { + "type": "named", + "name": "float8" + } + }, + "variance": { + "result_type": { + "type": "named", + "name": "float8" + } + } + }, + "comparison_operators": { + "_eq": { + "argument_type": { + "type": "named", + "name": "float4" + } + }, + "_gt": { + "argument_type": { + "type": "named", + "name": "float4" + } + }, + "_gte": { + "argument_type": { + "type": "named", + "name": "float4" + } + }, + "_lt": { + "argument_type": { + "type": "named", + "name": "float4" + } + }, + "_lte": { + "argument_type": { + "type": "named", + "name": "float4" + } + }, + "_neq": { + "argument_type": { + "type": "named", + "name": "float4" + } + } + }, + "update_operators": {} + }, + "float8": { + "aggregate_functions": { + "avg": { + "result_type": { + "type": "named", + "name": "float8" + } + }, + "max": { + "result_type": { + "type": "named", + "name": "float8" + } + }, + "min": { + "result_type": { + "type": "named", + "name": "float8" + } + }, + "stddev": { + "result_type": { + "type": "named", + "name": "float8" + } + }, + "stddev_pop": { + "result_type": { + "type": "named", + "name": "float8" + } + }, + "stddev_samp": { + "result_type": { + "type": "named", + "name": "float8" + } + }, + "sum": { + "result_type": { + "type": "named", + "name": "float8" + } + }, + "var_pop": { + "result_type": { + "type": "named", + "name": "float8" + } + }, + "var_samp": { + "result_type": { + "type": "named", + "name": "float8" + } + }, + "variance": { + "result_type": { + "type": "named", + "name": "float8" + } + } + }, + "comparison_operators": { + "_eq": { + "argument_type": { + "type": "named", + "name": "float8" + } + }, + "_gt": { + "argument_type": { + "type": "named", + "name": "float8" + } + }, + "_gte": { + "argument_type": { + "type": "named", + "name": "float8" + } + }, + "_lt": { + "argument_type": { + "type": "named", + "name": "float8" + } + }, + "_lte": { + "argument_type": { + "type": "named", + "name": "float8" + } + }, + "_neq": { + "argument_type": { + "type": "named", + "name": "float8" + } + } + }, + "update_operators": {} + }, + "int": { + "aggregate_functions": { + "avg": { + "result_type": { + "type": "named", + "name": "numeric" + } + }, + "bit_and": { + "result_type": { + "type": "named", + "name": "int" + } + }, + "bit_or": { + "result_type": { + "type": "named", + "name": "int" + } + }, + "bit_xor": { + "result_type": { + "type": "named", + "name": "int" + } + }, + "max": { + "result_type": { + "type": "named", + "name": "int" + } + }, + "min": { + "result_type": { + "type": "named", + "name": "int" + } + }, + "stddev": { + "result_type": { + "type": "named", + "name": "numeric" + } + }, + "stddev_pop": { + "result_type": { + "type": "named", + "name": "numeric" + } + }, + "stddev_samp": { + "result_type": { + "type": "named", + "name": "numeric" + } + }, + "sum": { + "result_type": { + "type": "named", + "name": "int8" + } + }, + "var_pop": { + "result_type": { + "type": "named", + "name": "numeric" + } + }, + "var_samp": { + "result_type": { + "type": "named", + "name": "numeric" + } + }, + "variance": { + "result_type": { + "type": "named", + "name": "numeric" + } + } + }, + "comparison_operators": { + "_eq": { + "argument_type": { + "type": "named", + "name": "int" + } + }, + "_gt": { + "argument_type": { + "type": "named", + "name": "int" + } + }, + "_gte": { + "argument_type": { + "type": "named", + "name": "int" + } + }, + "_lt": { + "argument_type": { + "type": "named", + "name": "int" + } + }, + "_lte": { + "argument_type": { + "type": "named", + "name": "int" + } + }, + "_neq": { + "argument_type": { + "type": "named", + "name": "int" + } + } + }, + "update_operators": {} + }, + "int2": { + "aggregate_functions": { + "avg": { + "result_type": { + "type": "named", + "name": "numeric" + } + }, + "bit_and": { + "result_type": { + "type": "named", + "name": "int2" + } + }, + "bit_or": { + "result_type": { + "type": "named", + "name": "int2" + } + }, + "bit_xor": { + "result_type": { + "type": "named", + "name": "int2" + } + }, + "max": { + "result_type": { + "type": "named", + "name": "int2" + } + }, + "min": { + "result_type": { + "type": "named", + "name": "int2" + } + }, + "stddev": { + "result_type": { + "type": "named", + "name": "numeric" + } + }, + "stddev_pop": { + "result_type": { + "type": "named", + "name": "numeric" + } + }, + "stddev_samp": { + "result_type": { + "type": "named", + "name": "numeric" + } + }, + "sum": { + "result_type": { + "type": "named", + "name": "int8" + } + }, + "var_pop": { + "result_type": { + "type": "named", + "name": "numeric" + } + }, + "var_samp": { + "result_type": { + "type": "named", + "name": "numeric" + } + }, + "variance": { + "result_type": { + "type": "named", + "name": "numeric" + } + } + }, + "comparison_operators": { + "_eq": { + "argument_type": { + "type": "named", + "name": "int2" + } + }, + "_gt": { + "argument_type": { + "type": "named", + "name": "int2" + } + }, + "_gte": { + "argument_type": { + "type": "named", + "name": "int2" + } + }, + "_lt": { + "argument_type": { + "type": "named", + "name": "int2" + } + }, + "_lte": { + "argument_type": { + "type": "named", + "name": "int2" + } + }, + "_neq": { + "argument_type": { + "type": "named", + "name": "int2" + } + } + }, + "update_operators": {} + }, + "int8": { + "aggregate_functions": { + "avg": { + "result_type": { + "type": "named", + "name": "numeric" + } + }, + "bit_and": { + "result_type": { + "type": "named", + "name": "int8" + } + }, + "bit_or": { + "result_type": { + "type": "named", + "name": "int8" + } + }, + "bit_xor": { + "result_type": { + "type": "named", + "name": "int8" + } + }, + "max": { + "result_type": { + "type": "named", + "name": "int8" + } + }, + "min": { + "result_type": { + "type": "named", + "name": "int8" + } + }, + "stddev": { + "result_type": { + "type": "named", + "name": "numeric" + } + }, + "stddev_pop": { + "result_type": { + "type": "named", + "name": "numeric" + } + }, + "stddev_samp": { + "result_type": { + "type": "named", + "name": "numeric" + } + }, + "sum": { + "result_type": { + "type": "named", + "name": "numeric" + } + }, + "var_pop": { + "result_type": { + "type": "named", + "name": "numeric" + } + }, + "var_samp": { + "result_type": { + "type": "named", + "name": "numeric" + } + }, + "variance": { + "result_type": { + "type": "named", + "name": "numeric" + } + } + }, + "comparison_operators": { + "_eq": { + "argument_type": { + "type": "named", + "name": "int8" + } + }, + "_gt": { + "argument_type": { + "type": "named", + "name": "int8" + } + }, + "_gte": { + "argument_type": { + "type": "named", + "name": "int8" + } + }, + "_lt": { + "argument_type": { + "type": "named", + "name": "int8" + } + }, + "_lte": { + "argument_type": { + "type": "named", + "name": "int8" + } + }, + "_neq": { + "argument_type": { + "type": "named", + "name": "int8" + } + } + }, + "update_operators": {} + }, + "name": { + "aggregate_functions": {}, + "comparison_operators": { + "_eq": { + "argument_type": { + "type": "named", + "name": "name" + } + }, + "_gt": { + "argument_type": { + "type": "named", + "name": "name" + } + }, + "_gte": { + "argument_type": { + "type": "named", + "name": "name" + } + }, + "_ilike": { + "argument_type": { + "type": "named", + "name": "name" + } + }, + "_iregex": { + "argument_type": { + "type": "named", + "name": "name" + } + }, + "_like": { + "argument_type": { + "type": "named", + "name": "name" + } + }, + "_lt": { + "argument_type": { + "type": "named", + "name": "name" + } + }, + "_lte": { + "argument_type": { + "type": "named", + "name": "name" + } + }, + "_neq": { + "argument_type": { + "type": "named", + "name": "name" + } + }, + "_nilike": { + "argument_type": { + "type": "named", + "name": "name" + } + }, + "_niregex": { + "argument_type": { + "type": "named", + "name": "name" + } + }, + "_nlike": { + "argument_type": { + "type": "named", + "name": "name" + } + }, + "_nregex": { + "argument_type": { + "type": "named", + "name": "name" + } + }, + "_regex": { + "argument_type": { + "type": "named", + "name": "name" + } + } + }, + "update_operators": {} + }, + "numeric": { + "aggregate_functions": { + "avg": { + "result_type": { + "type": "named", + "name": "numeric" + } + }, + "max": { + "result_type": { + "type": "named", + "name": "numeric" + } + }, + "min": { + "result_type": { + "type": "named", + "name": "numeric" + } + }, + "stddev": { + "result_type": { + "type": "named", + "name": "numeric" + } + }, + "stddev_pop": { + "result_type": { + "type": "named", + "name": "numeric" + } + }, + "stddev_samp": { + "result_type": { + "type": "named", + "name": "numeric" + } + }, + "sum": { + "result_type": { + "type": "named", + "name": "numeric" + } + }, + "var_pop": { + "result_type": { + "type": "named", + "name": "numeric" + } + }, + "var_samp": { + "result_type": { + "type": "named", + "name": "numeric" + } + }, + "variance": { + "result_type": { + "type": "named", + "name": "numeric" + } + } + }, + "comparison_operators": { + "_eq": { + "argument_type": { + "type": "named", + "name": "numeric" + } + }, + "_gt": { + "argument_type": { + "type": "named", + "name": "numeric" + } + }, + "_gte": { + "argument_type": { + "type": "named", + "name": "numeric" + } + }, + "_lt": { + "argument_type": { + "type": "named", + "name": "numeric" + } + }, + "_lte": { + "argument_type": { + "type": "named", + "name": "numeric" + } + }, + "_neq": { + "argument_type": { + "type": "named", + "name": "numeric" + } + } + }, + "update_operators": {} + }, + "text": { + "aggregate_functions": { + "max": { + "result_type": { + "type": "named", + "name": "text" + } + }, + "min": { + "result_type": { + "type": "named", + "name": "text" + } + } + }, + "comparison_operators": { + "_eq": { + "argument_type": { + "type": "named", + "name": "text" + } + }, + "_gt": { + "argument_type": { + "type": "named", + "name": "text" + } + }, + "_gte": { + "argument_type": { + "type": "named", + "name": "text" + } + }, + "_ilike": { + "argument_type": { + "type": "named", + "name": "text" + } + }, + "_iregex": { + "argument_type": { + "type": "named", + "name": "text" + } + }, + "_like": { + "argument_type": { + "type": "named", + "name": "text" + } + }, + "_lt": { + "argument_type": { + "type": "named", + "name": "text" + } + }, + "_lte": { + "argument_type": { + "type": "named", + "name": "text" + } + }, + "_neq": { + "argument_type": { + "type": "named", + "name": "text" + } + }, + "_nilike": { + "argument_type": { + "type": "named", + "name": "text" + } + }, + "_niregex": { + "argument_type": { + "type": "named", + "name": "text" + } + }, + "_nlike": { + "argument_type": { + "type": "named", + "name": "text" + } + }, + "_nregex": { + "argument_type": { + "type": "named", + "name": "text" + } + }, + "_regex": { + "argument_type": { + "type": "named", + "name": "text" + } + } + }, + "update_operators": {} + }, + "time": { + "aggregate_functions": { + "max": { + "result_type": { + "type": "named", + "name": "time" + } + }, + "min": { + "result_type": { + "type": "named", + "name": "time" + } + } + }, + "comparison_operators": { + "_eq": { + "argument_type": { + "type": "named", + "name": "time" + } + }, + "_gt": { + "argument_type": { + "type": "named", + "name": "time" + } + }, + "_gte": { + "argument_type": { + "type": "named", + "name": "time" + } + }, + "_lt": { + "argument_type": { + "type": "named", + "name": "time" + } + }, + "_lte": { + "argument_type": { + "type": "named", + "name": "time" + } + }, + "_neq": { + "argument_type": { + "type": "named", + "name": "time" + } + } + }, + "update_operators": {} + }, + "timestamp": { + "aggregate_functions": { + "max": { + "result_type": { + "type": "named", + "name": "timestamp" + } + }, + "min": { + "result_type": { + "type": "named", + "name": "timestamp" + } + } + }, + "comparison_operators": { + "_eq": { + "argument_type": { + "type": "named", + "name": "timestamp" + } + }, + "_gt": { + "argument_type": { + "type": "named", + "name": "timestamp" + } + }, + "_gte": { + "argument_type": { + "type": "named", + "name": "timestamp" + } + }, + "_lt": { + "argument_type": { + "type": "named", + "name": "timestamp" + } + }, + "_lte": { + "argument_type": { + "type": "named", + "name": "timestamp" + } + }, + "_neq": { + "argument_type": { + "type": "named", + "name": "timestamp" + } + } + }, + "update_operators": {} + }, + "timestamptz": { + "aggregate_functions": { + "max": { + "result_type": { + "type": "named", + "name": "timestamptz" + } + }, + "min": { + "result_type": { + "type": "named", + "name": "timestamptz" + } + } + }, + "comparison_operators": { + "_eq": { + "argument_type": { + "type": "named", + "name": "timestamptz" + } + }, + "_gt": { + "argument_type": { + "type": "named", + "name": "timestamptz" + } + }, + "_gte": { + "argument_type": { + "type": "named", + "name": "timestamptz" + } + }, + "_lt": { + "argument_type": { + "type": "named", + "name": "timestamptz" + } + }, + "_lte": { + "argument_type": { + "type": "named", + "name": "timestamptz" + } + }, + "_neq": { + "argument_type": { + "type": "named", + "name": "timestamptz" + } + } + }, + "update_operators": {} + }, + "timetz": { + "aggregate_functions": { + "max": { + "result_type": { + "type": "named", + "name": "timetz" + } + }, + "min": { + "result_type": { + "type": "named", + "name": "timetz" + } + } + }, + "comparison_operators": { + "_eq": { + "argument_type": { + "type": "named", + "name": "timetz" + } + }, + "_gt": { + "argument_type": { + "type": "named", + "name": "timetz" + } + }, + "_gte": { + "argument_type": { + "type": "named", + "name": "timetz" + } + }, + "_lt": { + "argument_type": { + "type": "named", + "name": "timetz" + } + }, + "_lte": { + "argument_type": { + "type": "named", + "name": "timetz" + } + }, + "_neq": { + "argument_type": { + "type": "named", + "name": "timetz" + } + } + }, + "update_operators": {} + }, + "uuid": { + "aggregate_functions": {}, + "comparison_operators": { + "_eq": { + "argument_type": { + "type": "named", + "name": "uuid" + } + }, + "_gt": { + "argument_type": { + "type": "named", + "name": "uuid" + } + }, + "_gte": { + "argument_type": { + "type": "named", + "name": "uuid" + } + }, + "_lt": { + "argument_type": { + "type": "named", + "name": "uuid" + } + }, + "_lte": { + "argument_type": { + "type": "named", + "name": "uuid" + } + }, + "_neq": { + "argument_type": { + "type": "named", + "name": "uuid" + } + } + }, + "update_operators": {} + }, + "varchar": { + "aggregate_functions": {}, + "comparison_operators": { + "_eq": { + "argument_type": { + "type": "named", + "name": "varchar" + } + }, + "_gt": { + "argument_type": { + "type": "named", + "name": "varchar" + } + }, + "_gte": { + "argument_type": { + "type": "named", + "name": "varchar" + } + }, + "_ilike": { + "argument_type": { + "type": "named", + "name": "varchar" + } + }, + "_iregex": { + "argument_type": { + "type": "named", + "name": "varchar" + } + }, + "_like": { + "argument_type": { + "type": "named", + "name": "varchar" + } + }, + "_lt": { + "argument_type": { + "type": "named", + "name": "varchar" + } + }, + "_lte": { + "argument_type": { + "type": "named", + "name": "varchar" + } + }, + "_neq": { + "argument_type": { + "type": "named", + "name": "varchar" + } + }, + "_nilike": { + "argument_type": { + "type": "named", + "name": "varchar" + } + }, + "_niregex": { + "argument_type": { + "type": "named", + "name": "varchar" + } + }, + "_nlike": { + "argument_type": { + "type": "named", + "name": "varchar" + } + }, + "_nregex": { + "argument_type": { + "type": "named", + "name": "varchar" + } + }, + "_regex": { + "argument_type": { + "type": "named", + "name": "varchar" + } + } + }, + "update_operators": {} + } + }, + "object_types": { + "Album": { + "description": "The record of all albums", + "fields": { + "AlbumId": { + "description": "The identifier of an album", + "type": { + "type": "named", + "name": "int" + } + }, + "ArtistId": { + "description": "The id of the artist that authored the album", + "type": { + "type": "named", + "name": "int" + } + }, + "Title": { + "description": "The title of an album", + "type": { + "type": "named", + "name": "varchar" + } + } + } + }, + "Artist": { + "description": "The record of all artists", + "fields": { + "ArtistId": { + "description": "The identifier of an artist", + "type": { + "type": "named", + "name": "int" + } + }, + "Name": { + "description": "The name of an artist", + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "varchar" + } + } + } + } + }, + "Customer": { + "description": "The record of all customers", + "fields": { + "Address": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "varchar" + } + } + }, + "City": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "varchar" + } + } + }, + "Company": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "varchar" + } + } + }, + "Country": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "varchar" + } + } + }, + "CustomerId": { + "description": "The identifier of customer", + "type": { + "type": "named", + "name": "int" + } + }, + "Email": { + "type": { + "type": "named", + "name": "varchar" + } + }, + "Fax": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "varchar" + } + } + }, + "FirstName": { + "description": "The first name of a customer", + "type": { + "type": "named", + "name": "varchar" + } + }, + "LastName": { + "description": "The last name of a customer", + "type": { + "type": "named", + "name": "varchar" + } + }, + "Phone": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "varchar" + } + } + }, + "PostalCode": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "varchar" + } + } + }, + "State": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "varchar" + } + } + }, + "SupportRepId": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "int" + } + } + } + } + }, + "Employee": { + "fields": { + "Address": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "varchar" + } + } + }, + "BirthDate": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "timestamp" + } + } + }, + "City": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "varchar" + } + } + }, + "Country": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "varchar" + } + } + }, + "Email": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "varchar" + } + } + }, + "EmployeeId": { + "type": { + "type": "named", + "name": "int" + } + }, + "Fax": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "varchar" + } + } + }, + "FirstName": { + "type": { + "type": "named", + "name": "varchar" + } + }, + "HireDate": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "timestamp" + } + } + }, + "LastName": { + "type": { + "type": "named", + "name": "varchar" + } + }, + "Phone": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "varchar" + } + } + }, + "PostalCode": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "varchar" + } + } + }, + "ReportsTo": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "int" + } + } + }, + "State": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "varchar" + } + } + }, + "Title": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "varchar" + } + } + } + } + }, + "Genre": { + "fields": { + "GenreId": { + "type": { + "type": "named", + "name": "int" + } + }, + "Name": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "varchar" + } + } + } + } + }, + "Invoice": { + "fields": { + "BillingAddress": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "varchar" + } + } + }, + "BillingCity": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "varchar" + } + } + }, + "BillingCountry": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "varchar" + } + } + }, + "BillingPostalCode": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "varchar" + } + } + }, + "BillingState": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "varchar" + } + } + }, + "CustomerId": { + "type": { + "type": "named", + "name": "int" + } + }, + "InvoiceDate": { + "type": { + "type": "named", + "name": "timestamp" + } + }, + "InvoiceId": { + "type": { + "type": "named", + "name": "int" + } + }, + "Total": { + "type": { + "type": "named", + "name": "numeric" + } + } + } + }, + "InvoiceLine": { + "fields": { + "InvoiceId": { + "type": { + "type": "named", + "name": "int" + } + }, + "InvoiceLineId": { + "type": { + "type": "named", + "name": "int" + } + }, + "Quantity": { + "type": { + "type": "named", + "name": "int" + } + }, + "TrackId": { + "type": { + "type": "named", + "name": "int" + } + }, + "UnitPrice": { + "type": { + "type": "named", + "name": "numeric" + } + } + } + }, + "MediaType": { + "fields": { + "MediaTypeId": { + "type": { + "type": "named", + "name": "int" + } + }, + "Name": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "varchar" + } + } + } + } + }, + "Playlist": { + "fields": { + "Name": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "varchar" + } + } + }, + "PlaylistId": { + "type": { + "type": "named", + "name": "int" + } + } + } + }, + "PlaylistTrack": { + "fields": { + "PlaylistId": { + "type": { + "type": "named", + "name": "int" + } + }, + "TrackId": { + "type": { + "type": "named", + "name": "int" + } + } + } + }, + "Track": { + "fields": { + "AlbumId": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "int" + } + } + }, + "Bytes": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "int" + } + } + }, + "Composer": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "varchar" + } + } + }, + "GenreId": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "int" + } + } + }, + "MediaTypeId": { + "type": { + "type": "named", + "name": "int" + } + }, + "Milliseconds": { + "type": { + "type": "named", + "name": "int" + } + }, + "Name": { + "type": { + "type": "named", + "name": "varchar" + } + }, + "TrackId": { + "type": { + "type": "named", + "name": "int" + } + }, + "UnitPrice": { + "type": { + "type": "named", + "name": "numeric" + } + } + } + }, + "album_by_title": { + "fields": { + "AlbumId": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "int" + } + } + }, + "ArtistId": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "int" + } + } + }, + "Title": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "varchar" + } + } + } + } + }, + "artist": { + "fields": { + "ArtistId": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "int" + } + } + }, + "Name": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "varchar" + } + } + } + } + }, + "artist_below_id": { + "fields": { + "ArtistId": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "int" + } + } + }, + "Name": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "varchar" + } + } + } + } + }, + "geography_columns": { + "fields": { + "coord_dimension": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "int" + } + } + }, + "f_geography_column": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "name" + } + } + }, + "f_table_catalog": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "name" + } + } + }, + "f_table_name": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "name" + } + } + }, + "f_table_schema": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "name" + } + } + }, + "srid": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "int" + } + } + }, + "type": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "text" + } + } + } + } + }, + "geometry_columns": { + "fields": { + "coord_dimension": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "int" + } + } + }, + "f_geometry_column": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "name" + } + } + }, + "f_table_catalog": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "varchar" + } + } + }, + "f_table_name": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "name" + } + } + }, + "f_table_schema": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "name" + } + } + }, + "srid": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "int" + } + } + }, + "type": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "varchar" + } + } + } + } + }, + "layer": { + "fields": { + "child_id": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "int" + } + } + }, + "feature_column": { + "type": { + "type": "named", + "name": "varchar" + } + }, + "feature_type": { + "type": { + "type": "named", + "name": "int" + } + }, + "layer_id": { + "type": { + "type": "named", + "name": "int" + } + }, + "level": { + "type": { + "type": "named", + "name": "int" + } + }, + "schema_name": { + "type": { + "type": "named", + "name": "varchar" + } + }, + "table_name": { + "type": { + "type": "named", + "name": "varchar" + } + }, + "topology_id": { + "type": { + "type": "named", + "name": "int" + } + } + } + }, + "spatial_ref_sys": { + "fields": { + "auth_name": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "varchar" + } + } + }, + "auth_srid": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "int" + } + } + }, + "proj4text": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "varchar" + } + } + }, + "srid": { + "type": { + "type": "named", + "name": "int" + } + }, + "srtext": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "varchar" + } + } + } + } + }, + "topology": { + "fields": { + "hasz": { + "type": { + "type": "named", + "name": "bool" + } + }, + "id": { + "type": { + "type": "named", + "name": "int" + } + }, + "name": { + "type": { + "type": "named", + "name": "varchar" + } + }, + "precision": { + "type": { + "type": "named", + "name": "float8" + } + }, + "srid": { + "type": { + "type": "named", + "name": "int" + } + } + } + }, + "value_types": { + "fields": { + "bool": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "bool" + } + } + }, + "char": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "char" + } + } + }, + "date": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "date" + } + } + }, + "float4": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "float4" + } + } + }, + "float8": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "float8" + } + } + }, + "int": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "int" + } + } + }, + "int2": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "int2" + } + } + }, + "int8": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "int8" + } + } + }, + "numeric": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "numeric" + } + } + }, + "text": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "text" + } + } + }, + "time": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "time" + } + } + }, + "timestamp": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "timestamp" + } + } + }, + "timestamptz": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "timestamptz" + } + } + }, + "timetz": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "timetz" + } + } + }, + "uuid": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "uuid" + } + } + }, + "varchar": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "varchar" + } + } + } + } + } + }, + "collections": [ + { + "name": "Album", + "description": "The record of all albums", + "arguments": {}, + "type": "Album", + "deletable": false, + "uniqueness_constraints": { + "PK_Album": { + "unique_columns": [ + "AlbumId" + ] + } + }, + "foreign_keys": { + "FK_AlbumArtistId": { + "column_mapping": { + "ArtistId": "ArtistId" + }, + "foreign_collection": "Artist" + } + } + }, + { + "name": "Artist", + "description": "The record of all artists", + "arguments": {}, + "type": "Artist", + "deletable": false, + "uniqueness_constraints": { + "PK_Artist": { + "unique_columns": [ + "ArtistId" + ] + } + }, + "foreign_keys": {} + }, + { + "name": "Customer", + "description": "The record of all customers", + "arguments": {}, + "type": "Customer", + "deletable": false, + "uniqueness_constraints": { + "PK_Customer": { + "unique_columns": [ + "CustomerId" + ] + } + }, + "foreign_keys": { + "FK_CustomerSupportRepId": { + "column_mapping": { + "SupportRepId": "EmployeeId" + }, + "foreign_collection": "Employee" + } + } + }, + { + "name": "Employee", + "arguments": {}, + "type": "Employee", + "deletable": false, + "uniqueness_constraints": { + "PK_Employee": { + "unique_columns": [ + "EmployeeId" + ] + } + }, + "foreign_keys": { + "FK_EmployeeReportsTo": { + "column_mapping": { + "ReportsTo": "EmployeeId" + }, + "foreign_collection": "Employee" + } + } + }, + { + "name": "Genre", + "arguments": {}, + "type": "Genre", + "deletable": false, + "uniqueness_constraints": { + "PK_Genre": { + "unique_columns": [ + "GenreId" + ] + } + }, + "foreign_keys": {} + }, + { + "name": "Invoice", + "arguments": {}, + "type": "Invoice", + "deletable": false, + "uniqueness_constraints": { + "PK_Invoice": { + "unique_columns": [ + "InvoiceId" + ] + } + }, + "foreign_keys": { + "FK_InvoiceCustomerId": { + "column_mapping": { + "CustomerId": "CustomerId" + }, + "foreign_collection": "Customer" + } + } + }, + { + "name": "InvoiceLine", + "arguments": {}, + "type": "InvoiceLine", + "deletable": false, + "uniqueness_constraints": { + "PK_InvoiceLine": { + "unique_columns": [ + "InvoiceLineId" + ] + } + }, + "foreign_keys": { + "FK_InvoiceLineInvoiceId": { + "column_mapping": { + "InvoiceId": "InvoiceId" + }, + "foreign_collection": "Invoice" + }, + "FK_InvoiceLineTrackId": { + "column_mapping": { + "TrackId": "TrackId" + }, + "foreign_collection": "Track" + } + } + }, + { + "name": "MediaType", + "arguments": {}, + "type": "MediaType", + "deletable": false, + "uniqueness_constraints": { + "PK_MediaType": { + "unique_columns": [ + "MediaTypeId" + ] + } + }, + "foreign_keys": {} + }, + { + "name": "Playlist", + "arguments": {}, + "type": "Playlist", + "deletable": false, + "uniqueness_constraints": { + "PK_Playlist": { + "unique_columns": [ + "PlaylistId" + ] + } + }, + "foreign_keys": {} + }, + { + "name": "PlaylistTrack", + "arguments": {}, + "type": "PlaylistTrack", + "deletable": false, + "uniqueness_constraints": { + "PK_PlaylistTrack": { + "unique_columns": [ + "PlaylistId", + "TrackId" + ] + } + }, + "foreign_keys": { + "FK_PlaylistTrackPlaylistId": { + "column_mapping": { + "PlaylistId": "PlaylistId" + }, + "foreign_collection": "Playlist" + }, + "FK_PlaylistTrackTrackId": { + "column_mapping": { + "TrackId": "TrackId" + }, + "foreign_collection": "Track" + } + } + }, + { + "name": "Track", + "arguments": {}, + "type": "Track", + "deletable": false, + "uniqueness_constraints": { + "PK_Track": { + "unique_columns": [ + "TrackId" + ] + } + }, + "foreign_keys": { + "FK_TrackAlbumId": { + "column_mapping": { + "AlbumId": "AlbumId" + }, + "foreign_collection": "Album" + }, + "FK_TrackGenreId": { + "column_mapping": { + "GenreId": "GenreId" + }, + "foreign_collection": "Genre" + }, + "FK_TrackMediaTypeId": { + "column_mapping": { + "MediaTypeId": "MediaTypeId" + }, + "foreign_collection": "MediaType" + } + } + }, + { + "name": "geography_columns", + "arguments": {}, + "type": "geography_columns", + "deletable": false, + "uniqueness_constraints": {}, + "foreign_keys": {} + }, + { + "name": "geometry_columns", + "arguments": {}, + "type": "geometry_columns", + "deletable": false, + "uniqueness_constraints": {}, + "foreign_keys": {} + }, + { + "name": "layer", + "arguments": {}, + "type": "layer", + "deletable": false, + "uniqueness_constraints": { + "layer_pkey": { + "unique_columns": [ + "layer_id", + "topology_id" + ] + }, + "layer_schema_name_table_name_feature_column_key": { + "unique_columns": [ + "feature_column", + "schema_name", + "table_name" + ] + } + }, + "foreign_keys": { + "layer_topology_id_fkey": { + "column_mapping": { + "topology_id": "id" + }, + "foreign_collection": "topology" + } + } + }, + { + "name": "spatial_ref_sys", + "arguments": {}, + "type": "spatial_ref_sys", + "deletable": false, + "uniqueness_constraints": { + "spatial_ref_sys_pkey": { + "unique_columns": [ + "srid" + ] + } + }, + "foreign_keys": {} + }, + { + "name": "topology", + "arguments": {}, + "type": "topology", + "deletable": false, + "uniqueness_constraints": { + "topology_name_key": { + "unique_columns": [ + "name" + ] + }, + "topology_pkey": { + "unique_columns": [ + "id" + ] + } + }, + "foreign_keys": {} + }, + { + "name": "album_by_title", + "arguments": { + "id": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "int" + } + } + }, + "title": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "varchar" + } + } + } + }, + "type": "album_by_title", + "deletable": false, + "uniqueness_constraints": {}, + "foreign_keys": {} + }, + { + "name": "artist", + "arguments": {}, + "type": "artist", + "deletable": false, + "uniqueness_constraints": {}, + "foreign_keys": {} + }, + { + "name": "artist_below_id", + "arguments": { + "id": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "int" + } + } + } + }, + "type": "artist_below_id", + "deletable": false, + "uniqueness_constraints": {}, + "foreign_keys": {} + }, + { + "name": "value_types", + "arguments": { + "bool": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "bool" + } + } + }, + "char": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "char" + } + } + }, + "date": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "date" + } + } + }, + "float4": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "float4" + } + } + }, + "float8": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "float8" + } + } + }, + "int": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "int" + } + } + }, + "int2": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "int2" + } + } + }, + "int8": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "int8" + } + } + }, + "numeric": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "numeric" + } + } + }, + "text": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "text" + } + } + }, + "time": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "time" + } + } + }, + "timestamp": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "timestamp" + } + } + }, + "timestamptz": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "timestamptz" + } + } + }, + "timetz": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "timetz" + } + } + }, + "uuid": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "uuid" + } + } + }, + "varchar": { + "type": { + "type": "nullable", + "underlying_type": { + "type": "named", + "name": "varchar" + } + } + } + }, + "type": "value_types", + "deletable": false, + "uniqueness_constraints": {}, + "foreign_keys": {} + } + ], "functions": [], "procedures": [] } From 07050704ece144a4b163345fa6da33790962b4a1 Mon Sep 17 00:00:00 2001 From: Daniel Harvey Date: Thu, 9 Nov 2023 17:24:14 +0000 Subject: [PATCH 7/7] Format --- crates/ndc-sqlserver/src/schema.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/ndc-sqlserver/src/schema.rs b/crates/ndc-sqlserver/src/schema.rs index 3040382e..57e11d1e 100644 --- a/crates/ndc-sqlserver/src/schema.rs +++ b/crates/ndc-sqlserver/src/schema.rs @@ -207,4 +207,3 @@ fn column_to_type(column: &metadata::ColumnInfo) -> models::Type { }, } } -