Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Oracle CDP with relationships #2677

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ internal class CdpTableResolver : ICdpTableResolver

private readonly bool _doubleEncoding;

private readonly JsonSerializerOptions _deserializationOptions = new ()
{
PropertyNameCaseInsensitive = true
};

// Temporary hack to generate ADS
public bool GenerateADS { get; init; }

Expand Down Expand Up @@ -83,6 +88,21 @@ public async Task<CdpTableDescriptor> ResolveTableAsync(string tableName, Cancel
// Result should be cached
sqlRelationships = GetSqlRelationships(text2);
}
else if (IsOracle())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IsOracle

Let's followup.
This code really ought to be server-side int he CDP implementation; and not client-side here.

{
cancellationToken.ThrowIfCancellationRequested();

uri = (_uriPrefix ?? string.Empty) + $"/datasets/{dataset}/query/oracle";
string body =
@"{""query"":""SELECT TAB_CONS.CONSTRAINT_NAME AS FK_Name, '[' || TAB_CONS.OWNER || '].[' || TAB_CONS.TABLE_NAME || ']' AS Parent_Table, TAB_CONS_COLS.COLUMN_NAME AS Parent_Column, '[' || REF_CONS.OWNER || '].[' || REF_CONS.TABLE_NAME || ']' AS Referenced_Table, REF_CONS_COLS.COLUMN_NAME AS Referenced_Column" +
@" FROM ALL_CONSTRAINTS TAB_CONS" +
@" INNER JOIN ALL_CONS_COLUMNS TAB_CONS_COLS ON TAB_CONS.CONSTRAINT_NAME = TAB_CONS_COLS.CONSTRAINT_NAME AND TAB_CONS.OWNER = TAB_CONS_COLS.OWNER" +
@" INNER JOIN ALL_CONSTRAINTS REF_CONS ON TAB_CONS.R_CONSTRAINT_NAME = REF_CONS.CONSTRAINT_NAME" +
@" INNER JOIN ALL_CONS_COLUMNS REF_CONS_COLS ON REF_CONS.CONSTRAINT_NAME = REF_CONS_COLS.CONSTRAINT_NAME AND REF_CONS.OWNER = REF_CONS_COLS.OWNER" +
@" WHERE '[' || TAB_CONS.OWNER || '].[' || TAB_CONS.TABLE_NAME || ']' = '" + tableName + "'" + @"""}";

sqlRelationships = GetSqlRelationships(await CdpServiceBase.GetObject(_httpClient, $"Get Oracle relationships", uri, body, cancellationToken, Logger).ConfigureAwait(false));
}

string connectorName = _uriPrefix.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries)[1];
ConnectorType ct = ConnectorFunction.GetConnectorTypeAndTableCapabilities(this, connectorName, "Schema/Items", FormulaValue.New(text), sqlRelationships, ConnectorCompatibility.CdpCompatibility, _tabularTable.DatasetName, out string name, out string displayName, out ServiceCapabilities tableCapabilities);
Expand All @@ -95,9 +115,11 @@ public async Task<CdpTableDescriptor> ResolveTableAsync(string tableName, Cancel

private bool IsSql() => _uriPrefix.Contains("/sql/");

private bool IsOracle() => _uriPrefix.Contains("/oracle/");

private List<SqlRelationship> GetSqlRelationships(string text)
{
RelationshipResult r = JsonSerializer.Deserialize<RelationshipResult>(text);
RelationshipResult r = JsonSerializer.Deserialize<RelationshipResult>(text, _deserializationOptions);

var relationships = r.ResultSets.Table1;
if (relationships == null || relationships.Length == 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Responses\OpenAI CreateImageEditWithMask.json" />
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Responses\OpenAI CreateImageVariation.json" />
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Responses\OpenAI CreateImageVariation2.json" />
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Responses\Oracle GetRelationships SampleDB.json" />
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Responses\Response_DVReturnType_00.json" />
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Responses\Response_DVReturnType_01.json" />
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Responses\Response_DVReturnType_02.json" />
Expand Down Expand Up @@ -293,6 +294,7 @@
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Swagger\Office_365_Outlook.json" />
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Swagger\Office_365_Users.json" />
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Swagger\OpenAI.yaml" />
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Swagger\Oracle.json" />
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Swagger\PetStore.json" />
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Swagger\PowerAppsForMakers.json" />
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Swagger\PowerPlatformForAdmins.json" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1588,6 +1588,77 @@ public async Task SQL_ExecuteStoredProc_WithUserAgent()
Assert.Equal<object>(expected, actual);
}

[Fact]
public async Task Oracle_GetRelationships()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oracle_GetRelationships

Need to add a test like SQL_CdpTabular_GetTables2 but with an Oracle connection

{
using var testConnector = new LoggingTestServer(@"Swagger\Oracle.json", _output);
var apiDoc = testConnector._apiDocument;
var config = new PowerFxConfig(Features.PowerFxV1);

using var httpClient = new HttpClient(testConnector);
string jwt = "eyJ0eXAiO...";
using var client = new PowerPlatformConnectorClient("4d4a8e81-17a4-4a92-9bfe-8d12e607fb7f.08.common.tip1.azure-apihub.net", "4d4a8e81-17a4-4a92-9bfe-8d12e607fb7f", "53f515b50c3e4925803ec1f0945e799f", () => jwt, httpClient) { SessionId = "8e67ebdc-d402-455a-b33a-304820832383" };

config.AddActionConnector(new ConnectorSettings("Oracle") { IncludeInternalFunctions = true, AllowUnsupportedFunctions = true }, apiDoc, new ConsoleLogger(_output));
RecalcEngine engine = new RecalcEngine(config);
RuntimeConfig rc = new RuntimeConfig().AddRuntimeContext(new TestConnectorRuntimeContext("Oracle", client, console: _output));

string query =
"SELECT TAB_CONS.CONSTRAINT_NAME AS FK_Name, '[' || TAB_CONS.OWNER || '].[' || TAB_CONS.TABLE_NAME || ']' AS Parent_Table, TAB_CONS_COLS.COLUMN_NAME AS Parent_Column, '[' || REF_CONS.OWNER || '].[' || REF_CONS.TABLE_NAME || ']' AS Referenced_Table, REF_CONS_COLS.COLUMN_NAME AS Referenced_Column" +
@" FROM ALL_CONSTRAINTS TAB_CONS" +
@" INNER JOIN ALL_CONS_COLUMNS TAB_CONS_COLS ON TAB_CONS.CONSTRAINT_NAME = TAB_CONS_COLS.CONSTRAINT_NAME AND TAB_CONS.OWNER = TAB_CONS_COLS.OWNER" +
@" INNER JOIN ALL_CONSTRAINTS REF_CONS ON TAB_CONS.R_CONSTRAINT_NAME = REF_CONS.CONSTRAINT_NAME" +
@" INNER JOIN ALL_CONS_COLUMNS REF_CONS_COLS ON REF_CONS.CONSTRAINT_NAME = REF_CONS_COLS.CONSTRAINT_NAME AND REF_CONS.OWNER = REF_CONS_COLS.OWNER" +
@" WHERE '[' || TAB_CONS.OWNER || '].[' || TAB_CONS.TABLE_NAME || ']' = '[HR].[EMPLOYEES]'";

testConnector.SetResponseFromFile(@"Responses\Oracle GetRelationships SampleDB.json");
var result = await engine.EvalAsync(@$"Oracle.ExecutePassThroughNativeQuery({{ query: ""{query}"" }})", CancellationToken.None, new ParserOptions() { AllowsSideEffects = true }, runtimeConfig: rc);

UntypedObjectValue uov = Assert.IsType<UntypedObjectValue>(result);
JsonUntypedObject juo = Assert.IsType<JsonUntypedObject>(uov.Impl);
JsonElement je = juo._element;

RelationshipResult r = je.Deserialize<RelationshipResult>(new JsonSerializerOptions()
{
PropertyNameCaseInsensitive = true
});
var relationships = r.ResultSets.Table1;

List<SqlRelationship> sqlRelationShips = new List<SqlRelationship>();

foreach (var fk in relationships)
{
sqlRelationShips.Add(new SqlRelationship()
{
RelationshipName = fk.FK_Name,
ParentTable = fk.Parent_Table,
ColumnName = fk.Parent_Column,
ReferencedTable = fk.Referenced_Table,
ReferencedColumnName = fk.Referenced_Column
});
}

Assert.Equal(3, sqlRelationShips.Count);
Assert.Equal("EMP_DEPT_FK, [HR].[EMPLOYEES], DEPARTMENT_ID, [HR].[DEPARTMENTS], DEPARTMENT_ID", sqlRelationShips[0].ToString());
Assert.Equal("EMP_JOB_FK, [HR].[EMPLOYEES], JOB_ID, [HR].[JOBS], JOB_ID", sqlRelationShips[1].ToString());
Assert.Equal("EMP_MANAGER_FK, [HR].[EMPLOYEES], MANAGER_ID, [HR].[EMPLOYEES], EMPLOYEE_ID", sqlRelationShips[2].ToString());

string expected = @$"POST https://4d4a8e81-17a4-4a92-9bfe-8d12e607fb7f.08.common.tip1.azure-apihub.net/invoke
authority: 4d4a8e81-17a4-4a92-9bfe-8d12e607fb7f.08.common.tip1.azure-apihub.net
Authorization: Bearer {jwt}
path: /invoke
scheme: https
x-ms-client-environment-id: /providers/Microsoft.PowerApps/environments/4d4a8e81-17a4-4a92-9bfe-8d12e607fb7f
x-ms-client-session-id: 8e67ebdc-d402-455a-b33a-304820832383
x-ms-request-method: POST
x-ms-request-url: /apim/oracle/53f515b50c3e4925803ec1f0945e799f/datasets/default/query/oracle
x-ms-user-agent: PowerFx/{PowerPlatformConnectorClient.Version}
[content-header] Content-Type: application/json; charset=utf-8
[body] {{""query"":""SELECT TAB_CONS.CONSTRAINT_NAME AS FK_Name, \u0027[\u0027 || TAB_CONS.OWNER || \u0027].[\u0027 || TAB_CONS.TABLE_NAME || \u0027]\u0027 AS Parent_Table, TAB_CONS_COLS.COLUMN_NAME AS Parent_Column, \u0027[\u0027 || REF_CONS.OWNER || \u0027].[\u0027 || REF_CONS.TABLE_NAME || \u0027]\u0027 AS Referenced_Table, REF_CONS_COLS.COLUMN_NAME AS Referenced_Column FROM ALL_CONSTRAINTS TAB_CONS INNER JOIN ALL_CONS_COLUMNS TAB_CONS_COLS ON TAB_CONS.CONSTRAINT_NAME = TAB_CONS_COLS.CONSTRAINT_NAME AND TAB_CONS.OWNER = TAB_CONS_COLS.OWNER INNER JOIN ALL_CONSTRAINTS REF_CONS ON TAB_CONS.R_CONSTRAINT_NAME = REF_CONS.CONSTRAINT_NAME INNER JOIN ALL_CONS_COLUMNS REF_CONS_COLS ON REF_CONS.CONSTRAINT_NAME = REF_CONS_COLS.CONSTRAINT_NAME AND REF_CONS.OWNER = REF_CONS_COLS.OWNER WHERE \u0027[\u0027 || TAB_CONS.OWNER || \u0027].[\u0027 || TAB_CONS.TABLE_NAME || \u0027]\u0027 = \u0027[HR].[EMPLOYEES]\u0027""}}
";
Assert.Equal(expected, testConnector._log.ToString());
}

private sealed class DateTimeNoTZ : BuiltinFunction, IAsyncTexlFunction5
{
public override ArgPreprocessor GetArgPreprocessor(int index, int argCount) => base.GetGenericArgPreprocessor(index);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"OutputParameters": {},
"ResultSets": {
"Table1": [
{
"FK_NAME": "EMP_DEPT_FK",
"PARENT_TABLE": "[HR].[EMPLOYEES]",
"PARENT_COLUMN": "DEPARTMENT_ID",
"REFERENCED_TABLE": "[HR].[DEPARTMENTS]",
"REFERENCED_COLUMN": "DEPARTMENT_ID"
},
{
"FK_NAME": "EMP_JOB_FK",
"PARENT_TABLE": "[HR].[EMPLOYEES]",
"PARENT_COLUMN": "JOB_ID",
"REFERENCED_TABLE": "[HR].[JOBS]",
"REFERENCED_COLUMN": "JOB_ID"
},
{
"FK_NAME": "EMP_MANAGER_FK",
"PARENT_TABLE": "[HR].[EMPLOYEES]",
"PARENT_COLUMN": "MANAGER_ID",
"REFERENCED_TABLE": "[HR].[EMPLOYEES]",
"REFERENCED_COLUMN": "EMPLOYEE_ID"
}
]
}
}
Loading