2012-07-03 15 views
7

जैसा कि एंड्रॉइड दस्तावेज़ कहते हैं, कच्चेQuery विधि के चयन ARR पैरामीटर स्ट्रिंग के रूप में पार्स किए जाते हैं।SQLite rawQuery selectArgs और Integers फ़ील्ड्स

SQLiteDatabase.rawQuery (स्ट्रिंग एसक्यूएल, String [] selectionArgs)

selectionArgs: आप शामिल हो सकते हैं में जहां क्वेरी, जो selectionArgs से मूल्यों से बदल दिया जाएगा में खंड?। मूल्य स्ट्रिंग्स के रूप में बाध्य होंगे।

लेकिन आज, मुझे एक समस्या का सामना करना पड़ा जिसने मेरे दिन का एक बड़ा हिस्सा लिया। निम्न क्वेरी की कल्पना करें:

SELECT * FROM TABLE_A WHERE IFNULL(COLUMN_A, 0) >= 15 

COLUMN_A INTEGER है। तालिका में लगभग 10 पंक्तियां हैं जो मानदंडों में भाग लेती हैं। डेटाबेस संपादक पर क्वेरी चलाना, परिणाम हमेशा सही था, लेकिन, स्मार्टफोन पर, कथन हमेशा कोई पंक्ति नहीं लौटाता था।

कुछ समय के बाद, एक करने के लिए क्वेरी बदल दिया है:

SELECT * FROM TABLE_A WHERE IFNULL(COLUMN_A, 0) >= '15' 

और संपादक सिर्फ एंड्रॉयड की तरह कोई भी पंक्ति वापस लौट आया। तो, क्वेरी को बदलना:

SELECT * FROM TABLE_A WHERE CAST(IFNULL(COLUMN_A, 0) as INTEGER) >= '15' 

समस्या हल हो गई। एक और परीक्षण किया गया था:

SELECT * FROM TABLE_A WHERE COLUMN_A >= '15' 

भी, सही परिणाम लौटा।

यह एक समस्या प्रतीत होती है जिसमें एंड्रॉइड IFNULL क्लॉज के साथ क्वेरी (स्ट्रिंग्स) के पैरामीटर को पैरामीटर से जोड़ता है।

तो, क्या कोई जानता है कि ऐसा क्यों हुआ? क्या क्वेरी पर CAST का उपयोग किए बिना इसे हल करने के लिए कोई सुझाव हैं?

+0

मुझे एक ही समस्या थी और मैंने अपने ऐप में एक त्रुटि खोजने का प्रयास करने में एक घंटे बिताए। तब मुझे आपकी पोस्ट मिली है। इस प्रकार यह वास्तव में महान है कि आपने इसे यहां लिखा है। एक और बात यह है कि मैं कभी एंड्रॉइड दार्शनिक नहीं समझूंगा और यह सीएएसटी क्यों जरूरी है .... – user2707175

उत्तर

9

कारण बाइंड आपके स्थान पर पहली बार क्वेरी के मान हैं, कि आप SQL-injection attack को रोकना चाहते हैं।

मूल रूप से, आप अपनी क्वेरी (प्लेसहोल्डर सहित) को अपने डेटाबेस में भेजते हैं और कहते हैं, "मेरी अगली क्वेरी इस फ़ॉर्म के होने जा रही है और कोई अन्य नहीं!"। जब कोई हमलावर तब एक अलग क्वेरी-स्ट्रिंग इंजेक्ट करता है (उदाहरण के लिए, फॉर्म-फ़ील्ड के माध्यम से) डेटाबेस कहता है "अरे, यह वह प्रश्न नहीं है जिसे आपने कहा था कि आप भेज देंगे!" और आप पर एक त्रुटि फेंकता है।

के बाद से आदेश (जो जुड़ा हुआ लेख में शो के रूप में अपने वास्तविक क्वेरी में इंजेक्ट किया जा सकता है) तार कर रहे हैं, स्ट्रिंग-डेटाप्रकार अधिक "खतरनाक" एक है। यदि कोई उपयोगकर्ता आपके फ़ील्ड में कुछ कोड इंजेक्ट करने का प्रयास करता है, तो केवल संख्याएं लेनी चाहिए और आप इनपुट को पूर्णांक में डालने/विश्लेषण करने का प्रयास करें (अपनी क्वेरी में मूल्य डालने से पहले), आपको तुरंत अपवाद मिलेगा। एक स्ट्रिंग के साथ, ऐसी कोई सुरक्षा नहीं है। इसके लिए, उन्हें शायद बच निकलना होगा। कारण हो सकता है कि बाइंड मान सभी स्ट्रिंग के रूप में व्याख्या किए जाते हैं।

ऊपर bongus है!यह पागल नहीं है अगर आपके द्वारा बाध्य किए गए तर्क तार या पूर्णांक हैं, तो वे सभी समान रूप से खतरनाक हैं। साथ ही, बहुत सारे बॉयलरप्लेट कोड में कोड में इन मानों को पूर्व-जांच कर रहे हैं, त्रुटि-प्रवण और unflexible है!


एसक्यूएल-इंजेक्शन से आपके आवेदन को रोकने और भी कई डेटाबेस लेखन संचालन, आप "तैयार बयान" का उपयोग करना चाहिए (एक ही आदेश है, लेकिन विभिन्न मूल्यों का प्रयोग करके) गति बढ़ाने के लिए। के लिए सही कक्षा एंड्रॉइड एसडीके में डेटाबेस में SQLiteStatement है।

एक तैयार बयान बनाने के लिए, आप अपने SQLiteDatabase -object की compileStatement()-method का उपयोग करें और बाँध संबंधित मानों (जो अपने प्रश्न में ? -marks से बदल दिया जाएगा) सही bindXX() -method (जो SQLiteProgram से लिए गए हैं का उपयोग कर):

SQLiteDatabase db = dbHelper.getWritableDatabase(); 
SQLiteStatement stmt = db.compileStatement("INSERT INTO SomeTable (name, age) values (?,?)"); 
// Careful! The index begins at 1, not 0 !! 
stmt.bindString(1, "Jon"); 
stmt.bindLong(2, 48L); 
stmt.execute(); 
// Also important! Clean up after yourself. 
stmt.close(); 

उदाहरण इस पुराने सवाल से लिया: How do I use prepared statements in SQlite in Android?


दुःख की बात है SQLiteStatement में एक अधिभार नहीं है जो Cursor देता है, इसलिए आप इसे SELECT -statements के लिए उपयोग नहीं कर सकते हैं।

int number = getNumberFromUser(); 
String[] arguments = new String[]{String.valueOf(number)}; 
db.rawQuery("SELECT * FROM TABLE_A WHERE IFNULL(COLUMN_A, 0) >= ?", arguments); 

ध्यान दें कि rawQuery() -method तर्क मूल्यों के लिए एक स्ट्रिंग सरणी लेता है: उन लोगों के लिए है, तो आप SQLiteDatabase की rawQuery(String, String[]) -method उपयोग कर सकते हैं। यह वास्तव में पागल नहीं है, SQLite प्रकार के बारे में कम परवाह नहीं कर सका। जब तक स्ट्रिंग-प्रस्तुतियां SQL क्वेरी में आप अपेक्षा करते हैं, तो आप ठीक हैं।

+1

हाय @ लुकास। आपकी प्रतिक्रिया के लिए धन्यवाद, लेकिन दस्तावेज़ों में कहा गया है कि तैयार कथन का उपयोग कर्सर को वापस करने के लिए नहीं किया जा सकता है। – regisxp

+0

@regisxp मुझे नहीं पता कि मैंने इसे क्यों नहीं देखा।मैंने अपना जवाब अपडेट किया। इसकी जांच - पड़ताल करें। –

+0

एंड्रॉइड कास्टिंग स्ट्रिंग 'स्ट्रिंग' क्यों है? आप संख्याओं के साथ एसक्यूएल प्रश्न इंजेक्ट नहीं कर सकते हैं, है ना? मैं चाहता हूं कि 'चयनआर्गमेंट्स पैरामीटर' ऑब्जेक्ट 'की एक सरणी थी और ढांचा पूर्णांक के रूप में पूर्णांक रखेगा, और इंजेक्शन से' स्ट्रिंग 'की रक्षा करेगा। –

1

मुझे लगता है कि मुझे लगता है कि एक ही समस्या है। जिस चीज का आप उल्लेख करने का प्रयास कर रहे हैं वह यह है कि आप स्क्लाइट डेटाबेस के बारे में वास्तविक गणना नहीं कर सकते हैं। मुझे पता चला है कि समस्या का उल्लेख आपके से बड़ा है। SQLite डेटाबेस फ़ील्ड मान से संबंधित किसी फ़ील्ड प्रकार को समझ में नहीं आता है। मतलब यह है कि यदि आप किसी इंटेग्रर फ़ील्ड में स्ट्रिंग मान डालने का प्रयास कर रहे हैं तो प्रविष्टि शिकायत नहीं करेगी।

तो समस्या जितनी बड़ी हो सकती है उतनी बड़ी है। हालांकि मैंने देखा है कि यदि आपके पास एक कॉलम है जिसमें केवल इंटीजर हैं और आप कहां से एक कथन बनाते हैं: जहां आईडी = 1 बिना '' परिणामस्वरूप डेटासेट होता है। तो मैं आपसे पूछ सकता हूं कि क्या आप सुनिश्चित हैं कि यह कथन काम नहीं करता है: "चुनें * टैबलेट_ए से जहां IFNULL (COLUMN_A, 0)> = 15"। लेकिन जहां id> = '15' काम करता है क्योंकि यह आईडी की स्ट्रिंग प्रस्तुति लेता है जो वास्तविक 2 यूनिकोड वर्ण (!!!) है और ऑपरेटर> = से '15' करने की कोशिश करता है जो लागू होता है।

पहली बार जब मैं इन मुद्दों पर आया तो मुझे आश्चर्य हुआ और मैंने गतिशील रूप से बाध्यकारी पैरामीटर के बिना एसक्यूएल बनाने और पूरे स्ट्रिंग के रूप में निष्पादित करने का निर्णय लिया है। मुझे पता है कि यह बाध्यकारी पैरामीटर के बिना डेटाबेस तक पहुंचने का सबसे अच्छा तरीका नहीं है, लेकिन यह एक समाधान है और एक अच्छा है क्योंकि सुरक्षा कारण महत्वपूर्ण नहीं हैं, हालांकि आपका डेटाबेस सुरक्षित है और इसे एक्सेस करने के आपके तरीके आपके आवेदन के लिए निजी हैं । यदि "घुसपैठिए" में रूट फोन द्वारा डेटाबेस तक पहुंचने की क्षमता है तो वह इसे सिर्फ SQLITE स्टूडियो में रख सकता है और वह किया जाता है।

+0

हाय @ 10 एस। आपके कमेंट के लिए धन्यवाद। मेरे ऐप का 'पहला संस्करण' उसी तकनीक का उपयोग करता है जिसे आपने मैन्युअल रूप से पैरामीटर को बदल दिया था। मैंने बदलने का फैसला किया क्योंकि मुझे ओएस से प्राप्त कुछ अलर्ट, मुझे SQLite कथन कैश को बेहतर बनाने के लिए क्वेरी पर तर्कों का उपयोग करने के लिए कहा। क्या आपको यह सलाह भी मिली? – regisxp

+0

नहीं, सौभाग्य से या नहीं, मैं अभी तक इस तरह के अलर्ट में नहीं आया हूं और मैं एक ऐसे व्यवसाय अनुप्रयोग पर काम कर रहा हूं जिसके लिए बहुत से "भारी" querries और joins, subqueries आदि के साथ मैन्युअल रूप से SQL पैरामीटर को प्रतिस्थापित करने की आवश्यकता है। तो मुझे लगता है कि मैं एंड्रॉइड में SQLite क्षमताओं को पूरा करने के लिए परीक्षण किया है। क्या आप मुझे एक संकेत देने के लिए इन चेतावनियों को पोस्ट कर सकते हैं? – 10s

+0

पुष्टिकरण? चर्चा? – 10s

संबंधित मुद्दे