W skrócie

Operator kolektora nie mógł dopisać pozycji do dokumentu wydania, a w rejestrze błędów pojawiały się wpisy „Invalid column name”. Przyczyną był kod JavaScript formularza: pole wyboru zwracało wpisany tekst zamiast identyfikatora odbiorcy. Poprawka pilnuje, by do zapytania trafiała tylko liczba.

Opisujemy błąd z jednego wdrożenia Studio WMS.net, który na kolektorze objawiał się wpisem „Invalid column name” w rejestrze błędów. Przyczyną nie było zapytanie, tylko kod JavaScript formularza. Tekst jest napisany dla działu IT i kierownika projektu i nie zawiera dosłownego kodu z wdrożenia.

Formularz wydania na kolektorze opisuje artykuł o wydaniu bezpośrednim WZB na kolektorze. Inny cichy błąd, który ujawnił się dopiero przy zatwierdzeniu dokumentu, opisuje artykuł o procedurze z QUOTED_IDENTIFIER OFF.

Objaw w rejestrze błędów

Operator kolektora z aplikacją magazynową Android nie mógł dopisać pozycji do dokumentu wydania. W rejestrze błędów pojawiały się wpisy „Invalid column name”, w których nazwą kolumny była urwana fraza wyszukiwania.

Wpisy dotyczyły dwóch zapytań: zapytania zapisującego pozycję i zapytania kontrolnego. Mechanizm działał w zdecydowanej większości przypadków, więc błąd pojawiał się tylko wtedy, gdy operator wpisał frazę i nie wybrał pozycji z listy.

Przyczyną nie była treść zapytania, tylko kod JavaScript formularza. Parametr odbiorcy to identyfikator liczbowy, a zapytanie celowo wstawia go bez apostrofów.

Co robi val() w jqxComboBox

Biblioteka jqWidgets nadpisuje w jQuery metodę val(). Dla pola jqxComboBox wywołanie zwraca wartość zaznaczonej pozycji, a gdy nic nie zaznaczono, zwraca tekst z pola wejściowego.

Kod formularza widzi więc w obu przypadkach tę samą odpowiedź, choć znaczy ona co innego. Kolejne stany powstają, gdy operator wybierze pozycję, a potem dopisze tekst do pola, albo wpisze ręcznie same cyfry. Tabela zestawia cztery stany pola.

Stan polaCo zwraca val()Co trafia do zapytania
Wybrano pozycję z listyIdentyfikator liczbowyPoprawna liczba
Wpisano tekst, nie wybrano pozycjiWpisany tekstGoły token, czyli nazwa kolumny. Błąd Invalid column name
Wybrano pozycję, potem dopisano tekstPoprzedni wybórStary identyfikator. Wariant, który został
Wpisano same cyfry bez wyboruWpisany ciąg cyfrLiczba, więc zapytanie działa. Skutek cichy. Wariant, który został
Schemat: pole wyboru z wpisaną frazą, wartość zwracana przez val(), walidacja identyfikatora i komunikat przy braku wyboru
Schemat zestawia trzy z czterech stanów pola z wartością zwracaną przez val() i pokazuje ścieżkę poprawki w trzech krokach: odczyt, sprawdzenie wzorca i komunikat przy braku wyboru.

Dlaczego tekst stał się nazwą kolumny

Gdy operator wpisał frazę i nie wybrał pozycji z listy, do zapytania trafiał wpisany tekst jako goły token. SQL Server odczytywał go jako nazwę kolumny, bo bez apostrofów to nie literał tekstowy, tylko identyfikator. Błąd nie wynikał więc z uszkodzonych danych w bazie, tylko z wartości przesłanej do zapytania.

Przykład z fikcyjnymi nazwami: operator wpisuje fragment alf, nie wybiera pozycji, a do zapytania trafia ten fragment.

SELECT nazwa
FROM odbiorcy
WHERE id = alf;
-- Invalid column name 'alf'.

Ta sama słabość była na drugiej ścieżce. Procedura obsługi zamknięcia listy sprawdzała tylko, czy wartość jest niepusta, więc wpisany tekst trafiał także do zapytania kontrolnego.

Parametr liczbowy wstawiany do zapytania bez apostrofów musi być liczbą. Wpisany tekst staje się wtedy nazwą kolumny.

Funkcja pilnująca identyfikatora

Poprawka to pomocnicza funkcja, która zwraca wartość tylko wtedy, gdy przechodzi wyrażenie regularne ^[0-9]+$. Wzorzec przepuszcza wyłącznie wartości złożone z cyfr, co wyklucza wpisany tekst. Wpisany ciąg samych cyfr przeszedłby tak samo jak identyfikator, więc kontrola nie dowodzi wyboru z listy. Procedura obsługi zamknięcia listy używa tej funkcji.

function identyfikatorLubPusty(v) {
  var t = String(v);
  return /^[0-9]+$/.test(t) ? t : '';
}

Tuż przed zapisem pozycji formularz sprawdza identyfikator odbiorcy. Gdy go brak, pokazuje komunikat „Wybierz odbiorce z listy!” i ustawia fokus na polu. Niczego wtedy nie zapisuje. Pisownię komunikatu podajemy tak, jak ją pokazuje program.

To ostatnia kontrola, a nie pierwsza. W typowym przepływie wpisany tekst cofa operatora do kroku odbiorcy już wcześniej i komunikat się nie pojawia.

Sama zamiana val() na inną metodę odczytu wartości kontrolki niczego by nie naprawiła, bo oba wywołania idą tą samą ścieżką. Sposób zgłaszania błędów w formularzach opisuje artykuł o komunikatach błędów w WMS.

Wdrożenie i sprawdzenie poprawki

Wdrożenie wymagało wgrania pliku i podbicia znacznika wersji skryptu w definicji transakcji. Bez tego terminale nadal ładują skrypt z pamięci podręcznej.

Poprawka dotyczy skryptu formularza, a nie zapytań, więc zapytania w bazie zostają bez zmian. Dlatego sprawdzamy dwa miejsca: plik skryptu na serwerze i znacznik wersji w definicji transakcji.

Test porównawczy na produkcji dał dwa wyniki. Przy wpisanym tekście stara ścieżka zwracała ten tekst, a nowa pusty ciąg. Po wyborze z listy obie zwracały identyfikator, więc nic się nie zepsuło.

Czego świadomie nie zrobiono

Rozważano zabezpieczenie w samym zapytaniu: konwersję TRY_CONVERT z zamianą pustego wyniku na zero. Odrzucono je. Zamieniłoby twardy błąd na cichy zapis pozycji bez odbiorcy, czyli uszkodzony dokument bez żadnego sygnału.

Cichy błąd jest trudniejszy do wykrycia niż wpis w rejestrze, bo nikt nie dostaje sygnału. Dlatego walidacja stoi przed wysłaniem pozycji, a zapytanie zachowuje się jak dotąd.

Wariant, który został

Dwóch wariantów poprawka nie obejmuje. W pierwszym operator wybiera pozycję z listy, a potem dopisuje tekst do pola. Kontrolka nadal zwraca poprzedni wybór, więc walidacja przepuszcza stary identyfikator. Dokument dostaje wtedy odbiorcę wybranego wcześniej, a nie tego, którego fragment widać w polu.

W drugim operator wpisuje ręcznie same cyfry i nie wybiera pozycji. Funkcja zwraca ten ciąg tak samo jak identyfikator, a do zapytania trafia on jako liczba. Zapytanie działa, więc nie ma błędu w rejestrze, a dokument może dostać przypadkowego odbiorcę. Skutek jest cichy.

Uwaga na przyszłość dotyczy bardzo dużych identyfikatorów. JavaScript zaokrągla duże liczby całkowite powyżej pewnego progu, więc odczytany identyfikator mógłby wskazać innego, istniejącego odbiorcę. Dotychczasowe identyfikatory mieszczą się poniżej tego progu.

Pytania do zadania na etapie analizy

Przed wdrożeniem formularzy z polami wyboru zadajemy cztery pytania. To pytania, a nie fakty o opisywanym wdrożeniu. Zakres analizy opisuje artykuł o analizie przedwdrożeniowej WMS.

  • Które pola wstawiają wartość do zapytania - i czy parametr jest liczbą, czy tekstem.
  • Co widzi operator bez wyboru - komunikat przy polu i brak zapisu.
  • Gdzie trafiają błędy zapytań - kto przegląda rejestr błędów i jak często.
  • Jak wdrażamy zmianę skryptu - czy znacznik wersji w definicji transakcji zmienia się razem z plikiem.

Pola formularzy i ich kontrole uzgadnia się w specyfikacji formularzy w arkuszu.

Czego ta strona nie opisuje

Strona opisuje mechanizm błędu, a nie nazwy plików, funkcji ani zapytań z wdrożenia. Nie podajemy liczby operatorów ani liczby wystąpień wpisów. Opis dotyczy formularza jednego magazynu klienta, a nie standardowej funkcji Studio WMS.net.

Pytania i odpowiedzi o polu wyboru i błędzie Invalid column name

Dlaczego błąd Invalid column name pojawił się przy zapisie pozycji?

Parametr odbiorcy to identyfikator liczbowy wstawiany do zapytania bez apostrofów. Gdy operator wpisał frazę i nie wybrał pozycji z listy, do zapytania trafiał wpisany tekst, a SQL Server odczytywał go jako nazwę kolumny.

Co zwraca val() dla pola jqxComboBox bez wybranej pozycji?

Biblioteka jqWidgets nadpisuje metodę val() w jQuery. Dla pola jqxComboBox bez zaznaczonej pozycji metoda zwraca tekst z pola wejściowego, a nie identyfikator.

Jak sprawdzić, że w polu wybrano pozycję z listy?

Samo wyrażenie ^[0-9]+$ nie dowodzi wyboru: przepuszcza wartości złożone z cyfr, co wyklucza wpisany tekst, ale ręcznie wpisany ciąg cyfr przeszedłby tak samo. Przy braku identyfikatora formularz tuż przed zapisem pokazuje komunikat „Wybierz odbiorce z listy!” i niczego nie zapisuje.

Dlaczego nie wystarczy TRY_CONVERT w zapytaniu?

Zabezpieczenie w zapytaniu zamieniłoby twardy błąd na cichy zapis pozycji bez odbiorcy. Powstałby uszkodzony dokument bez żadnego sygnału, więc to rozwiązanie odrzucono.

Co zostało nienaprawione w tej poprawce?

Dwa warianty. W pierwszym operator wybiera pozycję z listy, a potem dopisuje tekst do pola: kontrolka nadal zwraca poprzedni wybór, więc walidacja przepuszcza stary identyfikator. W drugim operator wpisuje same cyfry bez wyboru, a funkcja przepuszcza je jak identyfikator.

Słownik pojęć o polu wyboru i identyfikatorze w zapytaniu

Terminy używane przez dział IT i kierownika projektu, gdy pole wyboru w formularzu WMS zwraca tekst zamiast identyfikatora.

jqxComboBox
Pole wyboru z listą z biblioteki jqWidgets. Operator może wybrać pozycję z listy albo wpisać tekst w polu wejściowym.
Nadpisanie metody val()
Podmiana metody jQuery przez bibliotekę jqWidgets. Dla jqxComboBox zwraca ona wartość zaznaczonej pozycji, a bez zaznaczenia tekst z pola.
Identyfikator liczbowy
Wartość złożona z cyfr, która wskazuje rekord, na przykład odbiorcę. W opisywanym wdrożeniu zapytanie wstawia go bez apostrofów, więc wpisany ciąg cyfr działa jak identyfikator.
Walidacja po stronie klienta
Kontrola wartości w kodzie JavaScript formularza, przed wysłaniem zapytania. Tutaj sprawdza, czy identyfikator odbiorcy jest liczbą.
Cichy błąd
Błąd, który nie zatrzymuje pracy i nie zostawia sygnału, a psuje wynik. Takim błędem byłby zapis pozycji bez odbiorcy.
Goły token
Wartość wstawiona do zapytania bez apostrofów. SQL Server odczytuje ją jako nazwę kolumny, a nie jako tekst.
Znacznik wersji skryptu
Oznaczenie wersji pliku skryptu w definicji transakcji. Po zmianie pliku trzeba je podbić, bo inaczej terminale ładują skrypt z pamięci podręcznej.

Opisz nam formularz, który sprawia kłopot

Podaj, w którym formularzu WMS Twojej firmy pojawia się błąd zapisu i co wpisuje operator, a przygotujemy plan diagnozy na danych.