MADDEYE GÖRE SİPARİŞ olarak sıfıra bölün

Aug 19 2020

Bir web uygulamasından yeni bir PEN-Test sonucu aldım. Test uzmanı, bilgi almak için dinamik bir SQL ifadesinin ORDER BY cümlesini kullanabildiklerini belirtti . Sıralanması gereken sütunun adı, bir uygulama tarafından sorguya aktarılır. Test cihazı, benzer görünen karmaşık bir ifade içerecek şekilde değeri değiştirdi CASE WHEN ... THEN 1 ELSE 1/0 END.

Ancak, çok basit bir test durumu oluşturdum. Bir Oracle veritabanım ve bir SQLServer'ım var. Her ikisi de aynı tabloları içerir. Sorgularım

SELECT * FROM users ORDER BY 1/0

Bunu Oracle'da yürüttüğümde, sorgu düzgün çalışıyor. SQLServer'da bir hata alıyorum 8134 Divide by zero error encountered..

PEN-Tester, uygulamayı şu anda kullandığımdan farklı bir Oracle sunucusunda kullandığından ve Oracle'ın sonunda hatalar attığı gerçeğini kötüye kullandıklarını bildirdiklerinden, ORDER BY maddelerinin yürütülmesini engelleyen bir Oracle ayarı olup olmadığını merak ediyorum sıfıra bölme olarak değerlendir. Böyle bir ortam var mı? Oracle Version kullanımda mı? 12.1.0.2.0Hiçbir istisnanın olmadığı bir yerdeyim . SQLServer'da da böyle bir ayar var mı?

Tabi ki ifadenin üretimini değiştireceğim, bu yüzden iletilen değerlerin sıralama için uygun sütun takma adları olup olmadığını kontrol edecek.

Yanıtlar

4 BalazsPapp Aug 19 2020 at 02:33

Oracle:

order by 1/0 başarılı olur, çünkü bu cümle tek başına anlamsızdır, optimize edici onu ayrıştırma zamanında otomatik olarak sorgudan çıkarır ve hiçbir zaman çalıştırılmaz.

SQL> select username from t1 where username like 'SYS%' order by 1/0;

USERNAME
--------------------------------------------------------------
SYS
SYSTEM
SYS$UMF
SYSBACKUP
SYSRAC
SYSKM
SYSDG

7 rows selected.

SQL> select * from table(dbms_xplan.display_cursor);

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------
SQL_ID  cnnmg28k0vspg, child number 0
-------------------------------------
select username from t1 where username like 'SYS%' order by 1/0

Plan hash value: 3617692013

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |       |       |     3 (100)|          |
|*  1 |  TABLE ACCESS FULL| T1   |     1 |     9 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("USERNAME" LIKE 'SYS%')

Hiç sıralama yapılmadı.

Başka bir şey ekleyin ve başarısız olur:

SQL> select username from t1 where username like 'SYS%' order by 1/0, 1;
select username from t1 where username like 'SYS%' order by 1/0, 1
                                                             *
ERROR at line 1:
ORA-01476: divisor is equal to zero


SQL>

Veya:

SQL>  select username from t1 where username like 'SYS%' order by 1/0;

USERNAME
--------------------------------------------------------------------------------
SYS
SYSTEM
SYS$UMF
SYSBACKUP
SYSRAC
SYSKM
SYSDG

7 rows selected.

SQL> select /*+ opt_param('_optimizer_order_by_elimination_enabled', 'false') */ username from t1 where username like 'SYS%' order by 1/0;
select /*+ opt_param('_optimizer_order_by_elimination_enabled', 'false') */ username from t1 where username like 'SYS%' order by 1/0
                                                                                                                                  *
ERROR at line 1:
ORA-01476: divisor is equal to zero


SQL>

Ayrıca, değer ayrıştırma zamanında sabit değilse (örneğin, bir değişkendir):

SQL> variable B1 number
SQL> exec :B1 := 0;

PL/SQL procedure successfully completed.

SQL> select username from t1 where username like 'SYS%' order by 1/:B1;
select username from t1 where username like 'SYS%' order by 1/:B1
                                                             *
ERROR at line 1:
ORA-01476: divisor is equal to zero

SQL>
2 JoshDarnell Aug 19 2020 at 03:44

İçin 1/0özel olarak, aşağıdaki çalıştırarak hatayı önleyebilir SET(kargaşasına neden ve önemli hataları gizleyebilir kapalı ben bu ayarları dönüm olarak, sorununuza "çözüm" olarak bu tavsiye etmiyoruz O notu) ifadeleri:

SET ANSI_WARNINGS, ARITHABORT OFF;

ORDER BYOnun tarafından Seçim listesinde bir sütun belirterek fıkra destekleri göre sıralamak sıralı pozisyon. Başka bir deyişle, " ORDER BY 1", seçilen listedeki ilk öğeye göre sıralamak anlamına gelir.

Bu örnek , Microsoft'tan "AdventureWorks" örnek veritabanını kullanır:

SELECT p.BusinessEntityID, p.FirstName 
FROM Person.Person p 
ORDER BY 2;

SQL Server, sabit ifadeleri desteklemez :

SELECT p.BusinessEntityID, p.FirstName 
FROM Person.Person p 
ORDER BY 2-1;

Msg 408, Düzey 16, Durum 1, Satır 18
ORDER BY listesinde, konum 1'de sabit bir ifade ile karşılaşıldı.

Sizin durumunuzda 1/0sabit bir ifadedir. Ancak, bunu hesaplamak bir hatayla sonuçlanacağından, SQL Server bunu farklı şekilde ele alır. Bunu Itzik Ben Gan'ın belirleyici olmayan sıralı sıra numaraları adlı makalesinde okuyabilirsiniz :

Olan, bir yandan SQL Server'ın sabit bölme uygulamasında başarısız olması ve bu nedenle sıralamanın tek bir sabit olmayan bir ifadeye dayalı olmasıdır. Öte yandan, optimize edici, sıralama değerinin tüm satırlar için aynı olduğunu anlar, bu nedenle sıralama ifadesini tamamen yok sayar.

1/0Sorgunun sürümünü bu iki ayar kapalıyken çalıştırırsak, yürütme planında bunu görebilirsiniz :

SET ANSI_WARNINGS, ARITHABORT OFF;
GO
SET STATISTICS XML ON;
GO

SELECT p.BusinessEntityID, p.FirstName 
FROM Person.Person p 
ORDER BY 1/0;

Bu durumda, sıralama işlemi olmadığını görebilirsiniz. Compute Skaler hesaplamaya çalışır 1/0, ancak başarısız olur. Bu iki ayar kapalı olduğundan, "sıfıra bölme" hatası bastırılır, böylece sorgu tamamlanır (kesin olmayan bir sıralama düzeni ile).


Dinamik sıralama için daha iyi bir çözüm Erland Sommarskog'un T ‑ SQL'de Dinamik Arama Koşulları makalesinde tartışılmıştır . Bu çözümün özü CASE, kullanıcı girişi sıralama sütununu bilinen bir sütun değerine dönüştürmek için bir ifade kullanmaktır :

SELECT @sql += ' ORDER BY ' + 
               CASE @sortcol WHEN 'OrderID'      THEN 'o.OrderID'
                             WHEN 'EmplyoeeID'   THEN 'o.EmployeeID'
                             WHEN 'ProductID'    THEN 'od.ProductID'
                             WHEN 'CustomerName' THEN 'c.CompanyName'
                             WHEN 'ProductName'  THEN 'p.ProductName'
                             ELSE 'o.OrderID'
               END + CASE @isdesc WHEN 0 THEN ' ASC' ELSE ' DESC' END

Bu, beklenmeyen değerlerin sorgu yürütmeyi etkilemesini önler ve SQL enjeksiyonuna karşı korumaya yardımcı olur.

Birden çok veritabanı platformu arasında uyumluluğu sürdürmeye çalıştığınız için bu yaklaşımın sizin için ne kadar uygun olduğunu bilmiyorum.