mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-23 13:51:31 +00:00
@@ -7,6 +7,70 @@ function queryResult(columns: string[], rows: unknown[][]): QueryResult {
|
||||
return { columns, rows, affected_rows: 0, execution_time_ms: 0 };
|
||||
}
|
||||
|
||||
describe("MySQL routine execution SQL", () => {
|
||||
it("binds OUT parameters without requiring user input", () => {
|
||||
const sql = buildProcedureExecutionSqlFromValues({
|
||||
databaseType: "mysql",
|
||||
schema: "app",
|
||||
routineName: "double_value",
|
||||
parameters: [
|
||||
{ name: "p_input", dataType: "int", mode: "IN", ordinal: 1, value: "5" },
|
||||
{ name: "p_output", dataType: "int", mode: "OUT", ordinal: 2, value: "ignored" },
|
||||
],
|
||||
});
|
||||
|
||||
expect(sql).toBe(["SET @dbx_output_2 = NULL;", "CALL `double_value`(5, @dbx_output_2);", "SELECT @dbx_output_2 AS `p_output`;"].join("\n"));
|
||||
});
|
||||
|
||||
it("initializes INOUT variables and returns all output values", () => {
|
||||
const sql = buildProcedureExecutionSqlFromValues({
|
||||
databaseType: "mysql",
|
||||
schema: "app",
|
||||
routineName: "adjust_values",
|
||||
parameters: [
|
||||
{ name: "p_delta", dataType: "int", mode: "IN", ordinal: 1, value: "5" },
|
||||
{ name: "p_total", dataType: "int", mode: "INOUT", ordinal: 2, value: "10" },
|
||||
{ name: "p_status", dataType: "varchar(16)", mode: "OUT", ordinal: 3, value: "" },
|
||||
],
|
||||
});
|
||||
|
||||
expect(sql).toBe(["SET @dbx_output_2 = 10;", "SET @dbx_output_3 = NULL;", "CALL `adjust_values`(5, @dbx_output_2, @dbx_output_3);", "SELECT @dbx_output_2 AS `p_total`, @dbx_output_3 AS `p_status`;"].join("\n"));
|
||||
});
|
||||
|
||||
it("keeps input-only procedure calls unchanged", () => {
|
||||
expect(
|
||||
buildProcedureExecutionSqlFromValues({
|
||||
databaseType: "mysql",
|
||||
schema: "app",
|
||||
routineName: "save_value",
|
||||
parameters: [{ name: "p_value", dataType: "varchar(32)", mode: "IN", ordinal: 1, value: "O'Reilly" }],
|
||||
}),
|
||||
).toBe("CALL `save_value`('O''Reilly');");
|
||||
});
|
||||
|
||||
it("uses MySQL parameter modes to generate output bindings", () => {
|
||||
const parameters = routineParametersFromResult(
|
||||
queryResult(
|
||||
["name", "data_type", "mode", "ordinal", "has_default"],
|
||||
[
|
||||
["p_input", "int", "IN", 1, false],
|
||||
["p_output", "int", "OUT", 2, false],
|
||||
],
|
||||
),
|
||||
"mysql",
|
||||
);
|
||||
|
||||
expect(parameters.map((parameter) => parameter.mode)).toEqual(["IN", "OUT"]);
|
||||
expect(
|
||||
buildProcedureExecutionSqlFromValues({
|
||||
databaseType: "mysql",
|
||||
routineName: "double_value",
|
||||
parameters: parameters.map((parameter) => ({ ...parameter, value: parameter.mode === "IN" ? "5" : "" })),
|
||||
}),
|
||||
).toContain("CALL `double_value`(5, @dbx_output_2);");
|
||||
});
|
||||
});
|
||||
|
||||
describe("SQL Server routine execution SQL", () => {
|
||||
it("declares and selects OUT parameters", () => {
|
||||
const sql = buildProcedureExecutionSqlFromValues({
|
||||
|
||||
@@ -44,6 +44,9 @@ export function buildProcedureExecutionSqlFromValues(options: BuildRoutineExecut
|
||||
if (options.databaseType === "sqlserver") {
|
||||
return buildSqlServerProcedureExecutionSql(routine, sortedParameters);
|
||||
}
|
||||
if (options.databaseType === "mysql") {
|
||||
return buildMySqlProcedureExecutionSql(routine, sortedParameters);
|
||||
}
|
||||
const values = sortedParameters.filter((parameter) => shouldIncludeParameter(parameter));
|
||||
const useNamedArguments = shouldUseNamedArguments(options.databaseType, sortedParameters);
|
||||
if (options.databaseType === "oracle" || options.databaseType === "dameng" || options.databaseType === "oceanbase-oracle") {
|
||||
@@ -77,6 +80,34 @@ function sqlServerParameterName(name: string): string {
|
||||
return name.startsWith("@") ? name : `@${name}`;
|
||||
}
|
||||
|
||||
function buildMySqlProcedureExecutionSql(routine: string, sortedParameters: RoutineParameterValue[]): string {
|
||||
const outputBindings = new Map<RoutineParameterValue, { variableName: string; alias: string }>();
|
||||
const initializations: string[] = [];
|
||||
|
||||
sortedParameters.forEach((parameter, index) => {
|
||||
if (!returnsRoutineOutput(parameter)) return;
|
||||
const variableName = `@dbx_output_${index + 1}`;
|
||||
const initialValue = parameter.mode === "INOUT" ? routineParameterSqlValue("mysql", parameter) : "NULL";
|
||||
initializations.push(`SET ${variableName} = ${initialValue};`);
|
||||
outputBindings.set(parameter, {
|
||||
variableName,
|
||||
alias: quoteTableIdentifier("mysql", parameter.name.replace(/^@/, "") || `output_${index + 1}`),
|
||||
});
|
||||
});
|
||||
|
||||
const args = sortedParameters.flatMap((parameter) => {
|
||||
const outputBinding = outputBindings.get(parameter);
|
||||
if (outputBinding) return [outputBinding.variableName];
|
||||
if (!shouldIncludeParameter(parameter)) return [];
|
||||
return [routineParameterSqlValue("mysql", parameter)];
|
||||
});
|
||||
const statements = [...initializations, `CALL ${routine}(${args.join(", ")});`];
|
||||
if (outputBindings.size > 0) {
|
||||
statements.push(`SELECT ${[...outputBindings.values()].map(({ variableName, alias }) => `${variableName} AS ${alias}`).join(", ")};`);
|
||||
}
|
||||
return statements.join("\n");
|
||||
}
|
||||
|
||||
function buildSqlServerProcedureExecutionSql(routine: string, sortedParameters: RoutineParameterValue[]): string {
|
||||
const outputBindings = new Map<RoutineParameterValue, { variableName: string; alias: string }>();
|
||||
const declarations: string[] = [];
|
||||
|
||||
Reference in New Issue
Block a user