mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-07-13 00:13:33 +08:00
- 支持 SQL*Plus 斜杠分隔符后的可选分号,避免 Oracle 过程执行出空语句 - 光标落在过程异常尾部或斜杠分隔行时,仍选择完整 PL/SQL 定义执行 - 补充前端语句选择、QueryEditor 执行和后端 split/DBQueryMulti 回归测试
440 lines
13 KiB
Go
440 lines
13 KiB
Go
package app
|
||
|
||
import (
|
||
"reflect"
|
||
"testing"
|
||
)
|
||
|
||
func TestSplitSQLStatements_BasicSplit(t *testing.T) {
|
||
input := "SELECT 1; SELECT 2; SELECT 3"
|
||
got := splitSQLStatements(input)
|
||
want := []string{"SELECT 1", "SELECT 2", "SELECT 3"}
|
||
if !reflect.DeepEqual(got, want) {
|
||
t.Errorf("splitSQLStatements(%q) = %v, want %v", input, got, want)
|
||
}
|
||
}
|
||
|
||
func TestSplitSQLStatements_QuotedSemicolon(t *testing.T) {
|
||
input := `SELECT 'hello;world'; SELECT 2`
|
||
got := splitSQLStatements(input)
|
||
want := []string{`SELECT 'hello;world'`, "SELECT 2"}
|
||
if !reflect.DeepEqual(got, want) {
|
||
t.Errorf("splitSQLStatements(%q) = %v, want %v", input, got, want)
|
||
}
|
||
}
|
||
|
||
func TestSplitSQLStatements_LineComment(t *testing.T) {
|
||
input := "SELECT 1; -- this is a comment;\nSELECT 2"
|
||
got := splitSQLStatements(input)
|
||
want := []string{"SELECT 1", "-- this is a comment;\nSELECT 2"}
|
||
if !reflect.DeepEqual(got, want) {
|
||
t.Errorf("splitSQLStatements(%q) = %v, want %v", input, got, want)
|
||
}
|
||
}
|
||
|
||
func TestSplitSQLStatements_BlockComment(t *testing.T) {
|
||
input := "SELECT /* ; */ 1; SELECT 2"
|
||
got := splitSQLStatements(input)
|
||
want := []string{"SELECT /* ; */ 1", "SELECT 2"}
|
||
if !reflect.DeepEqual(got, want) {
|
||
t.Errorf("splitSQLStatements(%q) = %v, want %v", input, got, want)
|
||
}
|
||
}
|
||
|
||
func TestSplitSQLStatements_EmptyInput(t *testing.T) {
|
||
got := splitSQLStatements("")
|
||
if len(got) != 0 {
|
||
t.Errorf("splitSQLStatements(\"\") = %v, want empty slice", got)
|
||
}
|
||
}
|
||
|
||
func TestSplitSQLStatements_SingleStatement(t *testing.T) {
|
||
input := "SELECT * FROM users WHERE id = 1"
|
||
got := splitSQLStatements(input)
|
||
want := []string{"SELECT * FROM users WHERE id = 1"}
|
||
if !reflect.DeepEqual(got, want) {
|
||
t.Errorf("splitSQLStatements(%q) = %v, want %v", input, got, want)
|
||
}
|
||
}
|
||
|
||
func TestSplitSQLStatements_DollarQuoting(t *testing.T) {
|
||
input := "SELECT $tag$hello;world$tag$; SELECT 2"
|
||
got := splitSQLStatements(input)
|
||
want := []string{"SELECT $tag$hello;world$tag$", "SELECT 2"}
|
||
if !reflect.DeepEqual(got, want) {
|
||
t.Errorf("splitSQLStatements(%q) = %v, want %v", input, got, want)
|
||
}
|
||
}
|
||
|
||
func TestSplitSQLStatements_PostgresCreateFunctionDollarQuoting(t *testing.T) {
|
||
input := `CREATE OR REPLACE FUNCTION refresh_stats() RETURNS void AS $$
|
||
BEGIN
|
||
PERFORM refresh_now();
|
||
END;
|
||
$$ LANGUAGE plpgsql;
|
||
SELECT 2;`
|
||
got := splitSQLStatements(input)
|
||
want := []string{
|
||
`CREATE OR REPLACE FUNCTION refresh_stats() RETURNS void AS $$
|
||
BEGIN
|
||
PERFORM refresh_now();
|
||
END;
|
||
$$ LANGUAGE plpgsql`,
|
||
"SELECT 2",
|
||
}
|
||
if !reflect.DeepEqual(got, want) {
|
||
t.Errorf("splitSQLStatements(%q) = %#v, want %#v", input, got, want)
|
||
}
|
||
}
|
||
|
||
func TestSplitSQLStatements_FullWidthSemicolon(t *testing.T) {
|
||
input := "SELECT 1;SELECT 2"
|
||
got := splitSQLStatements(input)
|
||
want := []string{"SELECT 1", "SELECT 2"}
|
||
if !reflect.DeepEqual(got, want) {
|
||
t.Errorf("splitSQLStatements(%q) = %v, want %v", input, got, want)
|
||
}
|
||
}
|
||
|
||
func TestSplitSQLStatements_Backtick(t *testing.T) {
|
||
input := "SELECT `col;name` FROM t; SELECT 2"
|
||
got := splitSQLStatements(input)
|
||
want := []string{"SELECT `col;name` FROM t", "SELECT 2"}
|
||
if !reflect.DeepEqual(got, want) {
|
||
t.Errorf("splitSQLStatements(%q) = %v, want %v", input, got, want)
|
||
}
|
||
}
|
||
|
||
func TestSplitSQLStatements_TrailingSemicolon(t *testing.T) {
|
||
input := "SELECT 1; SELECT 2;"
|
||
got := splitSQLStatements(input)
|
||
want := []string{"SELECT 1", "SELECT 2"}
|
||
if !reflect.DeepEqual(got, want) {
|
||
t.Errorf("splitSQLStatements(%q) = %v, want %v", input, got, want)
|
||
}
|
||
}
|
||
|
||
func TestSplitSQLStatements_SQLEscapedQuote(t *testing.T) {
|
||
input := "SELECT 'it''s a test'; SELECT 2"
|
||
got := splitSQLStatements(input)
|
||
want := []string{"SELECT 'it''s a test'", "SELECT 2"}
|
||
if !reflect.DeepEqual(got, want) {
|
||
t.Errorf("splitSQLStatements(%q) = %v, want %v", input, got, want)
|
||
}
|
||
}
|
||
|
||
func TestSplitSQLStatements_SQLEscapedQuoteMultiple(t *testing.T) {
|
||
input := "INSERT INTO t VALUES ('O''Brien', 'it''s OK'); SELECT 1"
|
||
got := splitSQLStatements(input)
|
||
want := []string{"INSERT INTO t VALUES ('O''Brien', 'it''s OK')", "SELECT 1"}
|
||
if !reflect.DeepEqual(got, want) {
|
||
t.Errorf("splitSQLStatements(%q) = %v, want %v", input, got, want)
|
||
}
|
||
}
|
||
|
||
func TestSplitSQLStatements_OracleAnonymousBlock(t *testing.T) {
|
||
input := `BEGIN
|
||
INSERT INTO tmp_disable_trigger (table_name) VALUES ('t_memcard_reg');
|
||
UPDATE t_memcard_reg SET CARDLEVEL = 1 WHERE MEMCARDNO = '8032277312';
|
||
DELETE FROM tmp_disable_trigger WHERE table_name = 't_memcard_reg';
|
||
END;
|
||
SELECT 1 FROM dual;`
|
||
got := splitSQLStatements(input)
|
||
want := []string{
|
||
`BEGIN
|
||
INSERT INTO tmp_disable_trigger (table_name) VALUES ('t_memcard_reg');
|
||
UPDATE t_memcard_reg SET CARDLEVEL = 1 WHERE MEMCARDNO = '8032277312';
|
||
DELETE FROM tmp_disable_trigger WHERE table_name = 't_memcard_reg';
|
||
END;`,
|
||
"SELECT 1 FROM dual",
|
||
}
|
||
if !reflect.DeepEqual(got, want) {
|
||
t.Errorf("splitSQLStatements(%q) = %#v, want %#v", input, got, want)
|
||
}
|
||
}
|
||
|
||
func TestSplitSQLStatements_OracleDeclareBlock(t *testing.T) {
|
||
input := `DECLARE
|
||
v_count NUMBER;
|
||
BEGIN
|
||
SELECT COUNT(*) INTO v_count FROM t_memcard_reg;
|
||
UPDATE t_memcard_reg SET CARDLEVEL = v_count WHERE MEMCARDNO = '8032277312';
|
||
END;
|
||
SELECT 1 FROM dual;`
|
||
got := splitSQLStatements(input)
|
||
want := []string{
|
||
`DECLARE
|
||
v_count NUMBER;
|
||
BEGIN
|
||
SELECT COUNT(*) INTO v_count FROM t_memcard_reg;
|
||
UPDATE t_memcard_reg SET CARDLEVEL = v_count WHERE MEMCARDNO = '8032277312';
|
||
END;`,
|
||
"SELECT 1 FROM dual",
|
||
}
|
||
if !reflect.DeepEqual(got, want) {
|
||
t.Errorf("splitSQLStatements(%q) = %#v, want %#v", input, got, want)
|
||
}
|
||
}
|
||
|
||
func TestSplitSQLStatements_OracleCreateProcedureBlock(t *testing.T) {
|
||
input := `CREATE OR REPLACE PROCEDURE proc_tally2accept(
|
||
p_tallyacceptno IN t_tally_accept_h.acceptno%TYPE,
|
||
out_acceptno OUT t_accept_h.acceptno%TYPE
|
||
) IS
|
||
v_busno t_tally_accept_h.busno%TYPE;
|
||
v_count PLS_INTEGER;
|
||
BEGIN
|
||
SELECT COUNT(*) INTO v_count FROM t_tally_accept_h WHERE acceptno = p_tallyacceptno;
|
||
IF v_count > 0 THEN
|
||
out_acceptno := p_tallyacceptno;
|
||
END IF;
|
||
END;
|
||
SELECT 1 FROM dual;`
|
||
got := splitSQLStatements(input)
|
||
want := []string{
|
||
`CREATE OR REPLACE PROCEDURE proc_tally2accept(
|
||
p_tallyacceptno IN t_tally_accept_h.acceptno%TYPE,
|
||
out_acceptno OUT t_accept_h.acceptno%TYPE
|
||
) IS
|
||
v_busno t_tally_accept_h.busno%TYPE;
|
||
v_count PLS_INTEGER;
|
||
BEGIN
|
||
SELECT COUNT(*) INTO v_count FROM t_tally_accept_h WHERE acceptno = p_tallyacceptno;
|
||
IF v_count > 0 THEN
|
||
out_acceptno := p_tallyacceptno;
|
||
END IF;
|
||
END;`,
|
||
"SELECT 1 FROM dual",
|
||
}
|
||
if !reflect.DeepEqual(got, want) {
|
||
t.Errorf("splitSQLStatements(%q) = %#v, want %#v", input, got, want)
|
||
}
|
||
}
|
||
|
||
func TestSplitSQLStatements_OracleCreateProcedureSkipsSqlPlusSlashDelimiter(t *testing.T) {
|
||
input := `CREATE OR REPLACE PROCEDURE cproc_tzhssr_order2sale_new(
|
||
p_sourceid IN VARCHAR2
|
||
) IS
|
||
v_memcardno VARCHAR2(40);
|
||
v_ecnt NUMBER;
|
||
CURSOR cur_ware IS
|
||
SELECT d.goodsid, d.goodsqty
|
||
FROM t_order_d d
|
||
WHERE d.sourceid = p_sourceid;
|
||
BEGIN
|
||
FOR row_ware IN cur_ware LOOP
|
||
v_ecnt := row_ware.goodsqty;
|
||
END LOOP;
|
||
END;
|
||
/
|
||
SELECT 1 FROM dual;`
|
||
got := splitSQLStatements(input)
|
||
want := []string{
|
||
`CREATE OR REPLACE PROCEDURE cproc_tzhssr_order2sale_new(
|
||
p_sourceid IN VARCHAR2
|
||
) IS
|
||
v_memcardno VARCHAR2(40);
|
||
v_ecnt NUMBER;
|
||
CURSOR cur_ware IS
|
||
SELECT d.goodsid, d.goodsqty
|
||
FROM t_order_d d
|
||
WHERE d.sourceid = p_sourceid;
|
||
BEGIN
|
||
FOR row_ware IN cur_ware LOOP
|
||
v_ecnt := row_ware.goodsqty;
|
||
END LOOP;
|
||
END;`,
|
||
"SELECT 1 FROM dual",
|
||
}
|
||
if !reflect.DeepEqual(got, want) {
|
||
t.Errorf("splitSQLStatements(%q) = %#v, want %#v", input, got, want)
|
||
}
|
||
}
|
||
|
||
func TestSplitSQLStatements_OracleCreateProcedureKeepsCursorCaseExpression(t *testing.T) {
|
||
input := `CREATE OR REPLACE PROCEDURE proc_accept_to_add(
|
||
p_acceptno IN t_accept_h.acceptno%TYPE
|
||
) IS
|
||
CURSOR cur_store_same(p_ind s_sys_ini.inipara%TYPE) IS
|
||
SELECT si.compid, si.batid, si.wareid
|
||
FROM t_store_i si
|
||
ORDER BY CASE
|
||
WHEN p_ind = '1' THEN
|
||
to_char(si.invalidate - to_date('19700101', 'yyyymmdd'))
|
||
WHEN p_ind = '2' THEN
|
||
lpad(to_char(floor(si.wareqty)), 10, '0')
|
||
ELSE
|
||
to_char(si.batid)
|
||
END,si.batid;
|
||
BEGIN
|
||
NULL;
|
||
END;
|
||
/
|
||
SELECT 1 FROM dual;`
|
||
got := splitSQLStatements(input)
|
||
want := []string{
|
||
`CREATE OR REPLACE PROCEDURE proc_accept_to_add(
|
||
p_acceptno IN t_accept_h.acceptno%TYPE
|
||
) IS
|
||
CURSOR cur_store_same(p_ind s_sys_ini.inipara%TYPE) IS
|
||
SELECT si.compid, si.batid, si.wareid
|
||
FROM t_store_i si
|
||
ORDER BY CASE
|
||
WHEN p_ind = '1' THEN
|
||
to_char(si.invalidate - to_date('19700101', 'yyyymmdd'))
|
||
WHEN p_ind = '2' THEN
|
||
lpad(to_char(floor(si.wareqty)), 10, '0')
|
||
ELSE
|
||
to_char(si.batid)
|
||
END,si.batid;
|
||
BEGIN
|
||
NULL;
|
||
END;`,
|
||
"SELECT 1 FROM dual",
|
||
}
|
||
if !reflect.DeepEqual(got, want) {
|
||
t.Errorf("splitSQLStatements(%q) = %#v, want %#v", input, got, want)
|
||
}
|
||
}
|
||
|
||
func TestSplitSQLStatements_OracleCreateProcedureSkipsCommentedSqlPlusSlashDelimiter(t *testing.T) {
|
||
input := `-- 修改函数/存储过程:H2.cproc_tzhssr_order2sale_A1
|
||
-- 请确认语法兼容当前数据库后执行
|
||
CREATE OR REPLACE PROCEDURE cproc_tzhssr_order2sale_A1(
|
||
p_sourceid IN VARCHAR2,
|
||
p_saleno_out OUT VARCHAR2,
|
||
p_msg_out OUT NVARCHAR2
|
||
) AS
|
||
v_saleno VARCHAR2(40);
|
||
v_ecnt NUMBER;
|
||
BEGIN
|
||
SELECT COUNT(*) INTO v_ecnt FROM dual;
|
||
p_saleno_out := v_saleno;
|
||
p_msg_out := 'OK';
|
||
EXCEPTION
|
||
WHEN OTHERS THEN
|
||
p_msg_out := SQLERRM;
|
||
END cproc_tzhssr_order2sale_A1;
|
||
/ -- SQLPlus delimiter from PL/SQL tools
|
||
SELECT 1 FROM dual;`
|
||
got := splitSQLStatements(input)
|
||
want := []string{
|
||
`-- 修改函数/存储过程:H2.cproc_tzhssr_order2sale_A1
|
||
-- 请确认语法兼容当前数据库后执行
|
||
CREATE OR REPLACE PROCEDURE cproc_tzhssr_order2sale_A1(
|
||
p_sourceid IN VARCHAR2,
|
||
p_saleno_out OUT VARCHAR2,
|
||
p_msg_out OUT NVARCHAR2
|
||
) AS
|
||
v_saleno VARCHAR2(40);
|
||
v_ecnt NUMBER;
|
||
BEGIN
|
||
SELECT COUNT(*) INTO v_ecnt FROM dual;
|
||
p_saleno_out := v_saleno;
|
||
p_msg_out := 'OK';
|
||
EXCEPTION
|
||
WHEN OTHERS THEN
|
||
p_msg_out := SQLERRM;
|
||
END cproc_tzhssr_order2sale_A1;`,
|
||
"SELECT 1 FROM dual",
|
||
}
|
||
if !reflect.DeepEqual(got, want) {
|
||
t.Errorf("splitSQLStatements(%q) = %#v, want %#v", input, got, want)
|
||
}
|
||
}
|
||
|
||
func TestSplitSQLStatements_OracleCreateProcedureSkipsSemicolonAfterSqlPlusSlashDelimiter(t *testing.T) {
|
||
input := `-- 修改函数/存储过程:H2.cproc_tzhssr_order2sale_A1
|
||
-- 请确认语法兼容当前数据库后执行
|
||
CREATE OR REPLACE PROCEDURE cproc_tzhssr_order2sale_A1(
|
||
p_sourceid IN VARCHAR2,
|
||
p_msg_out OUT NVARCHAR2
|
||
) AS
|
||
v_ecnt NUMBER;
|
||
BEGIN
|
||
SELECT COUNT(*) INTO v_ecnt FROM dual;
|
||
p_msg_out := '';
|
||
EXCEPTION
|
||
WHEN OTHERS THEN
|
||
p_msg_out := substr('订单核销失败,错误信息:' || SQLERRM || ',错误位置:' ||
|
||
dbms_utility.format_error_backtrace, 1, 1000);
|
||
END cproc_tzhssr_order2sale_A1;
|
||
/;
|
||
SELECT 1 FROM dual;`
|
||
got := splitSQLStatements(input)
|
||
want := []string{
|
||
`-- 修改函数/存储过程:H2.cproc_tzhssr_order2sale_A1
|
||
-- 请确认语法兼容当前数据库后执行
|
||
CREATE OR REPLACE PROCEDURE cproc_tzhssr_order2sale_A1(
|
||
p_sourceid IN VARCHAR2,
|
||
p_msg_out OUT NVARCHAR2
|
||
) AS
|
||
v_ecnt NUMBER;
|
||
BEGIN
|
||
SELECT COUNT(*) INTO v_ecnt FROM dual;
|
||
p_msg_out := '';
|
||
EXCEPTION
|
||
WHEN OTHERS THEN
|
||
p_msg_out := substr('订单核销失败,错误信息:' || SQLERRM || ',错误位置:' ||
|
||
dbms_utility.format_error_backtrace, 1, 1000);
|
||
END cproc_tzhssr_order2sale_A1;`,
|
||
"SELECT 1 FROM dual",
|
||
}
|
||
if !reflect.DeepEqual(got, want) {
|
||
t.Errorf("splitSQLStatements(%q) = %#v, want %#v", input, got, want)
|
||
}
|
||
}
|
||
|
||
func TestSplitSQLStatements_OraclePackageSpecAndBodyStayWhole(t *testing.T) {
|
||
input := `CREATE OR REPLACE PACKAGE pkg_order AS
|
||
PROCEDURE sync_order(p_id IN NUMBER);
|
||
END pkg_order;
|
||
/
|
||
CREATE OR REPLACE PACKAGE BODY pkg_order AS
|
||
PROCEDURE sync_order(p_id IN NUMBER) IS
|
||
BEGIN
|
||
NULL;
|
||
END sync_order;
|
||
END pkg_order;
|
||
/ -- SQLPlus delimiter from PL/SQL tools
|
||
SELECT 1 FROM dual;`
|
||
got := splitSQLStatements(input)
|
||
want := []string{
|
||
`CREATE OR REPLACE PACKAGE pkg_order AS
|
||
PROCEDURE sync_order(p_id IN NUMBER);
|
||
END pkg_order;`,
|
||
`CREATE OR REPLACE PACKAGE BODY pkg_order AS
|
||
PROCEDURE sync_order(p_id IN NUMBER) IS
|
||
BEGIN
|
||
NULL;
|
||
END sync_order;
|
||
END pkg_order;`,
|
||
"SELECT 1 FROM dual",
|
||
}
|
||
if !reflect.DeepEqual(got, want) {
|
||
t.Errorf("splitSQLStatements(%q) = %#v, want %#v", input, got, want)
|
||
}
|
||
}
|
||
|
||
func TestSplitSQLStatements_DoesNotTreatSlashOperatorLineAsDelimiter(t *testing.T) {
|
||
input := "SELECT 10\n/\n2 FROM dual;"
|
||
got := splitSQLStatements(input)
|
||
want := []string{"SELECT 10\n/\n2 FROM dual"}
|
||
if !reflect.DeepEqual(got, want) {
|
||
t.Errorf("splitSQLStatements(%q) = %#v, want %#v", input, got, want)
|
||
}
|
||
}
|
||
|
||
func TestSplitSQLStatements_TransactionBeginStillSplits(t *testing.T) {
|
||
input := "BEGIN; UPDATE accounts SET balance = balance - 1 WHERE id = 1; COMMIT;"
|
||
got := splitSQLStatements(input)
|
||
want := []string{
|
||
"BEGIN",
|
||
"UPDATE accounts SET balance = balance - 1 WHERE id = 1",
|
||
"COMMIT",
|
||
}
|
||
if !reflect.DeepEqual(got, want) {
|
||
t.Errorf("splitSQLStatements(%q) = %#v, want %#v", input, got, want)
|
||
}
|
||
}
|