known-issues-in-stored-procedures.md 37.4 KB

Known issues in stored procedures and functions

A consolidated index of code-level defects, comment drift, sealed-but-still-bound routines, and other audit signals that the auto-catalog narratives surfaced while documenting xly's 1,684 stored procedures and 177 functions.

What this page is, and is not

Is: an index. Each entry points at a routine whose per-routine auto-catalog page carries the full evidence (proc body excerpt, calling-channel analysis, etc.). The categories below are decision-support — they let a maintainer scan for what to triage first without reading 1,861 pages.

Is not: a fix plan. None of these defects have been fixed; the procedures are still running in production. Several are operationally invisible (e.g., a broken regex that always returns "yes" looks fine if the caller never relied on the filter). Others are likely intentional (sealed legacy procs kept around for rollback). Audit each entry before acting on it — a flag here is a signal worth verifying, not a confirmed bug ticket.

Methodology. Items were captured by the narrative-writing subagents when they read the proc/function body to understand what each routine did. The agents weren't running a defect audit — they noticed mismatches and bugs as a side effect. Pass 3 spot-checked 10 randomly-selected flag claims and all 10 verified against the live SHOW CREATE output. The list is high-signal but incomplete: routines whose body the agents didn't read in depth (huge bodies with body-summarization mode) may carry undocumented issues.


Real correctness / runtime bugs

These will produce wrong results, return wrong values, or fail at runtime under the documented conditions. Highest priority for audit.

  • Fun_GetCh — regex '[u0391-uFFE5]' is taken literally (matches u/0/3/…), not as the intended Unicode range Α-¥. The "non-Chinese filter" matches nothing; only -/+ stripping survives. Used by Sp_Create_sControlFaceNameTable*.
  • GetChineseChar — same broken [u0391-uFFE5] literal regex; classifies every character as non-Chinese.
  • GetDispatchUnit — falls back to eleproduct.sProductUnit only for one hardcoded GUID; returns NULL on any other deployment.
  • GetPriorProcess — one branch references session vars @uGuid/@sCurProcessName instead of proc-local uGuid/p_sCurProcessName; that branch always sees NULL.
  • PRO_ERPMERGEBASEELECUSTOMER — SET clauses swap sCustomerName = @sCustomerNo and sCustomerNo = @sCustomerName; label columns are written to the wrong fields.
  • PRO_ERPMERGEMFTWORKORDER, PRO_ERPMERGEPRODUCTIONREPORT, PRO_ERPMERGESALSALESORDERDELETE FROM <table> WHERE sSlaveId = sSlaveId. IN parameter name shadows the column → tautology → wipes the entire merge table on every call before the single-row re-INSERT.
  • Sp_AdjustMoney_BtnRepair — empty-memo guard sets sCode=-1 but does not LEAVE top; the empty-memo branch still applies the update.
  • Sp_AfterPost_sQtt — cursor inside iFlag=1 is bounded by viw_mftworkorderprocess count, but the inner SELECT reads from viw_quoquotation × quoquotationprocess × quoquotationcontrol; loop bound is wrong.
  • Sp_addBtn_gdsmodle — BtnExamine NOT EXISTS guard checks sControlName='BtnBsOperation.BtnInvalid' instead of BtnExamine.
  • Sp_addSysbrandsgdsconfigformslave clone reuses seed sId values rather than generating new ones; cross-tenant collision risk.
  • Sp_Apply_Flow_materialspd, Sp_Apply_Flow_productpdWHERE iPosition=32 AND (sEmployeeNo='00034' OR iPosition=7) is malformed; the iPosition=7 branch is unreachable.
  • Sp_Apply_Flow_OpsProductMorep_iCount2 calculated but iOut2 set from p_iCount4; variable-name drift.
  • Sp_Apply_Flow_OpsProductMorebecomep_ThreeFlow set to 总经理 then immediately overwritten by 财务总鉴 → 总经理 tier never returned. p_iCount3 assigned twice; p_iCount4 referenced but never assigned, so iOut2 is always 0.
  • Sp_Apply_Flow_PurCheck — variance formula multiplies new-price by new-price (B.dMaterialsPrice*A.dMaterialsPrice); likely should be dAuxiliaryQty*dMaterialsPrice.
  • Sp_Apply_Flow_PurpurchaseOrderp_iOut1 count of guidance-priced lines lacks WHERE sParentId=sGuId; counts across all orders, not just this one. Threshold 30000 hardcoded but header says 20000.
  • Sp_Apply_Flow_SalSaleOrderp_ElevenFlow and p_TwelveFlow both populate from iPosition=34 (duplicate); p_MoreFlow = p_ThreeFlow so 会签 collapses to a single party.
  • Sp_Apply_Flow_SalSalesNotify — 烟包 count lookup hits salsalesinvoiceslave instead of saldelivernotifyslave; iOut2 is wrong for delivery-notify forms with no companion invoice.
  • Sp_Apply_Flow_Ystz — emits "EightFlow", p_SevenFlow (assigns 总经理 value to 董事长 slot); p_EightFlow populated but never emitted.
  • Sp_Bd_Root_allstatus26 — temp-table seeding INSERTs/SELECTs column sMachineNo that the CREATE TEMPORARY TABLE doesn't define. Proc fails at every call.
  • Sp_beforeSaveReturn_sQtt — body joins pitproductrejectslave in a quotation back-writer; copy-paste from Sp_saveReturn_sPdt.
  • Sp_BtnEvent_AccProductUnFrozensFrozenPerson/tFrozenDate overwritten with the unfreezing user/timestamp, losing original-freeze attribution.
  • Sp_BtnEventCopyUserJurisdiction, Sp_BtnEventCopyGroupJurisdiction — loop-overwrite bug; only the last (sUserId, sToUserId) pair is acted on.
  • Sp_BtnUpdate_OnOpsConfirm, Sp_BtnUpdate_OnOpsOrder, Sp_BtnUpdate_OnPurOrderWHERE bOutConfirm=0 guard means once a row is confirmed the CASE toggle can never run again; effectively one-way confirm.
  • Sp_BtnUpdate_RevSupply — WHERE/SET column mismatch: guards on bCheck=0 but mutates bSubCheck. Copy-paste from sibling template.
  • Sp_Calc_plateprocessingIF iCount > 0 reads an undefined session variable (local is p_iCount); "already checked" guard never fires.
  • Sp_Calc_Product, Sp_Calc_sAcod — uncheck branch reads sNoCanCheckBybCheck instead of sNoCanCheckBybUnCheck.
  • Sp_Calc_about, Sp_Calc_borrow, Sp_Calc_Change, Sp_Calc_clearsTmpReturn declared but never assigned; trailing IF sTmpReturn <> '' branch dead across the family.
  • Sp_Calc_pruOrderBgd — accepts iFlag parameter but never branches on it; uncheck path missing entirely.
  • Sp_Calc_sExp — missing sStatus-flip and missing already-checked/unchecked guards versus family norm; both branches idempotent.
  • Sp_Calc_sMat — embedded Sp_System_CheckFlow loop scans SalSalesOrderSlave instead of any elematerials slave; copy-paste from 销售订单 template applied to a material-master audit.
  • Sp_Calc_sMcd — unqualified sMaterialsId in the UPDATE SET expression; affects 盘点 profit/loss columns on multi-line counts.
  • Sp_Calc_sPrw — un-audit guard reads from salsalesdevmaster rather than the production-report table.
  • Sp_Calc_tice, Sp_Calc_tore — column name mis-cased as sCheckPersON; case-sensitive on case-sensitive collations.
  • Sp_history_purpurchaseordermaster — cursor declares Cur_OrderSlave_handle1 = 1 (not 0); first row silently skipped (off-by-one).
  • Sp_MaterialHistoricalComparison — accepts sBrId/sSuId but never filters by tenant; every tenant's data mixes.
  • Sp_PurpurchaseOrder_CheckUpdate — name+COMMENT say "保存校验" but body is dOldPrice/Money back-write, not validation.
  • Sp_PurchasePriceChart — opens cursor over DT but DT is never populated; loop runs zero times.
  • Sp_Sales_SalesAnalysiseOfCustomerproperty — receipts subquery groups by EP.sParentId instead of EP.sCustomerPropertyId; axis mismatch.
  • Sp_Sales_SalesAnalysiseOfMonth, Sp_Sales_SalesAnalysiseProductOfMonthIFNULL(p_iStatisticsType, 0) self-reference typo; input parameter iStatisticsType is ignored.
  • Sp_Sales_SalesAnalysiseOfProfit, Sp_Sales_SalesAnalysiseOfProfit_Product — swapped-target rate columns: in-store rate writes to delivery's column and vice versa.
  • Sp_SalOrder_BillForceComplete — captured p_sReason parsed but never written.
  • Stuff — right-slice uses LOCATE(SUBSTR(str, startIndex, length), str); when the deleted substring also appears earlier in str, slices from the earlier occurrence.
  • sp_cursor_test1UPDATE eleteststandarditem SET iOrder=(@i:=@i+1) has no ORDER BY; row numbering is non-deterministic.

Header / comment drift

Cosmetic only — the routine works correctly but its ROUTINE_COMMENT, header comment, or page title describes a different routine. Common after a routine was renamed or cloned from a template without updating the header.

Sealed-but-still-bound (封存 markers retained alongside live bindings)

These procs carry author-written 封存 (sealed/archived) markers in the body header but still have live form-master or gdsmodule bindings. Either the marker is informational only (the proc is the canonical one), or the binding needs to be retired alongside the seal. Verify per routine before changes.

Tenant-baked GUIDs and hardcoded magic values

Routines that hardcode specific tenant/customer-specific sIds, dept GUIDs, PLC numbers, or magic numerics. Cloning to a new deployment requires per-routine re-mapping; these will silently produce empty/NULL/wrong output on a different tenant.

Dead code and unimplemented stubs

Procs that exist as empty bodies, single-statement placeholders, or developer scratch never wired in. Safe to delete from a documentation/lint perspective; verify no customer override calls them before doing so.

Debug leakage left in production

Stray SELECT … FROM bbbbb; or commented-out -- select aa from bb; left inside routine bodies. Mostly harmless (the commented-out forms emit nothing, the live forms emit a stray result-set caller may ignore) but a maintainer cleanup target.

Naming and structural inconsistencies

Security-adjacent

  • sp_btn_action — dynamic UPDATE concatenates JSON-decoded p_sId without QUOTE()-escaping; SQL injection surface if sProInParam isn't validated upstream.
  • Sp_interface_login_before — K3 Cloud credentials hardcoded in proc body (username='Administrator', password='Hamah@2022'). Multi-tenant deployments must replace.
  • Sp_afterSave_sDgda — raw PREPARE/EXECUTE dynamic SQL, splits on ; with no escaping.

Cross-cutting observations

A few patterns worth noting that aren't per-routine flags:

  • 封存 (sealed) markers are not deletion markers. Many sealed routines still have live form-master or gdsmodule bindings. The convention in this codebase is "keep the body but mark intent"; if you act on a 封存 marker, also unbind the form.
  • Customer overrides live at xly-src/script/客户/<customer>/. Several routines that look like simple stubs in the standard ship (e.g. Sp_Bill_Used_Base) have substantive bodies in customer-override packs. Don't delete a stub before checking script/客户/.
  • _new/_NEW/_Bak/_Test/date-suffixed variants are a recurring pattern: a parallel rewrite never promoted to production, kept alongside the wired version. The auto-catalog's Backup / snapshot variant template covers the basic _copy1/_bak family; the _new/_NEW siblings live here.
  • Naming-by-convention dispatchers are invisible to static analysis. BusinessBaseServiceImpl.checkUpdate(..., "sSaveProName") reads the proc name out of gdsmodule at runtime; GenericProcedureCallServiceImpl.doGenericProcedureCall() reads it from the front-end button action descriptor. The auto-catalog narratives capture these where the agents found them, but the snapshot used here doesn't include front-end button config rows — some routines flagged as orphan in this list may be live via a button binding we couldn't observe.