Firebase Data Connect Cloud SQL डेटाबेस के साथ इंटरैक्ट करने के कई तरीके उपलब्ध कराता है:
- नेटिव GraphQL: अपनी
schema.gqlमें टाइप तय करें. इसके बाद, Data Connect GraphQL के आपके ऑपरेशन्स को SQL में बदल देगा. यह स्टैंडर्ड तरीका है. इसमें, टाइपिंग और स्कीमा-एनफ़ोर्स्ड स्ट्रक्चर की सुविधा मिलती है. इस पेज के अलावा, Data Connect ज़्यादातर दस्तावेज़ों में इस विकल्प के बारे में बताया गया है. अगर मुमकिन हो, तो आपको इस तरीके का इस्तेमाल करना चाहिए, ताकि टाइप की पूरी सुरक्षा और टूलिंग की सुविधा का फ़ायदा मिल सके. - **
@viewडायरेक्टिव**:schema.gqlमें GraphQL टाइप तय करें. यह कस्टमSELECTSQL स्टेटमेंट पर आधारित होता है. यह, जटिल SQL लॉजिक के आधार पर, सिर्फ़ पढ़ने के लिए, मज़बूत टाइप वाले व्यू बनाने में मददगार है. इन टाइप के लिए, सामान्य टाइप की तरह क्वेरी की जा सकती है.@viewदेखें. - नेटिव SQL: में, नाम वाले
ऑपरेशन्स में सीधे तौर पर SQL स्टेटमेंट एम्बेड करें .खास रूट फ़ील्ड का इस्तेमाल करके,
gqlफ़ाइलें. इससे ज़्यादा फ़्लेक्सिबिलिटी और डायरेक्ट कंट्रोल मिलता है. खास तौर पर, उन ऑपरेशन्स के लिए जिन्हें स्टैंडर्ड GraphQL में आसानी से नहीं दिखाया जा सकता. साथ ही, डेटाबेस की खास सुविधाओं का इस्तेमाल किया जा सकता है या PostgreSQL एक्सटेंशन का इस्तेमाल किया जा सकता है. GraphQL और@viewडायरेक्टिव के उलट, नेटिव SQL से मज़बूत टाइप वाला आउटपुट नहीं मिलता.
इस गाइड में, नेटिव SQL विकल्प पर फ़ोकस किया गया है.
नेटिव SQL के इस्तेमाल के सामान्य उदाहरण
नेटिव GraphQL से टाइप की पूरी सुरक्षा मिलती है. वहीं, @view डायरेक्टिव से, सिर्फ़ पढ़ने के लिए SQL रिपोर्ट के लिए मज़बूत टाइप वाले नतीजे मिलते हैं. नेटिव SQL से, इन कामों के लिए ज़रूरी फ़्लेक्सिबिलिटी मिलती है:
- PostgreSQL एक्सटेंशन: इंस्टॉल किए गए किसी भी PostgreSQL
एक्सटेंशन (जैसे, भौगोलिक डेटा के लिए
PostGIS) के लिए सीधे तौर पर क्वेरी करें और उसका इस्तेमाल करें. इसके लिए, GraphQL स्कीमा में जटिल टाइप को मैप करने की ज़रूरत नहीं होती. - जटिल क्वेरी: जॉइन, सबक्वेरी, एग्रीगेशन, विंडो फ़ंक्शन, और सेव किए गए प्रोसीज़र के साथ जटिल SQL को एक्ज़ीक्यूट करें.
- डेटा मैनिप्युलेशन (डीएमएल):
INSERT, UPDATE, DELETEऑपरेशन्स सीधे तौर पर करें. (हालांकि, डेटा डेफ़िनिशन लैंग्वेज (डीडीएल) कमांड के लिए, नेटिव SQL का इस्तेमाल न करें. बैकएंड और जनरेट किए गए एसडीके को सिंक में रखने के लिए, आपको GraphQL का इस्तेमाल करके स्कीमा-लेवल में बदलाव करने होंगे.) - डेटाबेस की खास सुविधाएं: PostgreSQL के लिए यूनीक फ़ंक्शन, ऑपरेटर या डेटा टाइप का इस्तेमाल करें.
- परफ़ॉर्मेंस ऑप्टिमाइज़ेशन: अहम पाथ के लिए, SQL स्टेटमेंट को मैन्युअल तरीके से ट्यून करें.
नेटिव SQL के रूट फ़ील्ड
SQL की मदद से ऑपरेशन्स लिखने के लिए, query या mutation टाइप के इनमें से किसी एक रूट फ़ील्ड का इस्तेमाल करें:
query फ़ील्ड
| फ़ील्ड | ब्यौरा |
|---|---|
_select |
SQL क्वेरी को एक्ज़ीक्यूट करता है. इससे कोई भी लाइन या एक से ज़्यादा लाइनें दिखती हैं. आर्गुमेंट:
नतीजा: JSON अरे ( |
_selectFirst |
SQL क्वेरी को एक्ज़ीक्यूट करता है. इससे कोई भी लाइन या एक लाइन दिखती है. आर्गुमेंट:
नतीजा: JSON ऑब्जेक्ट ( |
mutation फ़ील्ड
| फ़ील्ड | ब्यौरा |
|---|---|
_execute |
डीएमएल स्टेटमेंट ( आर्गुमेंट:
नतीजा: नतीजे में, |
_executeReturning |
आर्गुमेंट:
नतीजा: JSON अरे ( |
_executeReturningFirst |
आर्गुमेंट:
नतीजा: JSON ऑब्जेक्ट ( |
ध्यान दें:
ऑपरेशन्स, Data Connect सेवा खाते को दी गई अनुमतियों का इस्तेमाल करके एक्ज़ीक्यूट किए जाते हैं.
अगर आपने
@tableडायरेक्टिव (@table(name: "ExampleTable")) का इस्तेमाल करके, टेबल का नाम साफ़ तौर पर सेट किया है, तो आपको SQL स्टेटमेंट (SELECT field FROM "ExampleTable" ...) में भी टेबल के नाम को कोट में रखना होगा.कोटेशन मार्क के बिना, Data Connect टेबल के नाम को स्नेक केस (
example_table) में बदल देगा.
सिंटैक्स के नियम और सीमाएं
नेटिव SQL, सुरक्षा पक्का करने और SQL इंजेक्शन से बचने के लिए, पार्सिंग के सख्त नियमों को लागू करता है. कृपया यहां बताई गई बातों का ध्यान रखें:
- टिप्पणियां: ब्लॉक टिप्पणियों (
/* ... */) का इस्तेमाल करें. लाइन टिप्पणियों (--) का इस्तेमाल नहीं किया जा सकता, क्योंकि क्वेरी को एक साथ जोड़ने के दौरान, ये बाद के क्लॉज़ (जैसे, सुरक्षा फ़िल्टर) को काट सकती हैं. - पैरामीटर: पोज़िशनल पैरामीटर (
$1,$2) का इस्तेमाल करें. येparamsअरे के क्रम से मेल खाते हैं. नाम वाले पैरामीटर ($id,:name) इस्तेमाल नहीं किए जा सकते. - स्ट्रिंग: एक्सटेंडेड स्ट्रिंग लिटरल (
E'...') और डॉलर-कोटेड स्ट्रिंग ($$...$$) इस्तेमाल किए जा सकते हैं. PostgreSQL Unicode एस्केप (U&'...') इस्तेमाल नहीं किए जा सकते.
टिप्पणियों में पैरामीटर
पार्सर, ब्लॉक टिप्पणी में मौजूद हर चीज़ को अनदेखा कर देता है. अगर आपने किसी पैरामीटर वाली लाइन पर टिप्पणी की है (उदाहरण के लिए, /* WHERE id = $1 */), तो आपको उस पैरामीटर को params सूची से भी हटाना होगा. ऐसा न करने पर, ऑपरेशन में गड़बड़ी होगी. साथ ही, unused parameter: $1 मैसेज दिखेगा.
उदाहरण
पहला उदाहरण: फ़ील्ड एलियासिंग के साथ सामान्य SELECT
क्लाइंट रिस्पॉन्स को साफ़-सुथरा बनाने के लिए, रूट फ़ील्ड का एलियास बनाया जा सकता है. उदाहरण के लिए, movies: _select. इससे data._select के बजाय data.movies दिखेगा.
queries.gql:
query GetMoviesByGenre($genre: String!, $limit:Int!) @auth(level: PUBLIC) {
movies: _select(
sql: """
SELECT id, title, release_year, rating
FROM movie
WHERE genre = $1
ORDER BY release_year DESC
LIMIT $2
""",
params: [$genre, $limit]
)
}
क्लाइंट एसडीके का इस्तेमाल करके क्वेरी चलाने के बाद, नतीजा data.movies में दिखेगा.
दूसरा उदाहरण: सामान्य UPDATE
mutations.gql:
mutation UpdateMovieRating($movieId: UUID!, $newRating: Float!) @auth(level: NO_ACCESS) {
_execute(
sql: """
UPDATE movie
SET rating = $2
WHERE id = $1
""",
params: [$movieId, $newRating]
)
}
क्लाइंट एसडीके का इस्तेमाल करके म्यूटेशन चलाने के बाद, असर डालने वाली लाइनों की संख्या data._execute में दिखेगी.
तीसरा उदाहरण: सामान्य एग्रीगेशन
queries.gql:
query GetTotalReviewCount @auth(level: PUBLIC) {
stats: _selectFirst(
sql: "SELECT COUNT(*) as total_reviews FROM \"Reviews\""
)
}
क्लाइंट एसडीके का इस्तेमाल करके क्वेरी चलाने के बाद, नतीजा data.stats.total_reviews में दिखेगा.
चौथा उदाहरण: RANK के साथ अडवांस एग्रीगेशन
queries.gql:
query GetMoviesRankedByRating @auth(level: PUBLIC) {
_select(
sql: """
SELECT
id,
title,
rating,
RANK() OVER (ORDER BY rating DESC) as rank
FROM movie
WHERE rating IS NOT NULL
LIMIT 20
""",
params: []
)
}
क्लाइंट एसडीके का इस्तेमाल करके क्वेरी चलाने के बाद, नतीजा data._select में दिखेगा.
पांचवा उदाहरण: RETURNING और ऑथ कॉन्टेक्स्ट के साथ UPDATE
mutations.gql:
mutation UpdateMyReviewText($movieId: UUID!, $newText: String!) @auth(level: USER) {
updatedReview: _executeReturningFirst(
sql: """
UPDATE "Reviews"
SET review_text = $2
WHERE movie_id = $1 AND user_id = $3
RETURNING movie_id, user_id, rating, review_text
""",
params: [$movieId,$newText,{_expr: "auth.uid" }]
)
}
क्लाइंट एसडीके का इस्तेमाल करके म्यूटेशन चलाने के बाद, अपडेट किया गया पोस्ट डेटा data.updatedReview में दिखेगा.
छठा उदाहरण: अपसर्ट (एटॉमिक गेट-ऑर-क्रिएट) के साथ अडवांस सीटीई
इस पैटर्न का इस्तेमाल, डिपेंडेंट रिकॉर्ड (जैसे, उपयोगकर्ता या फ़िल्में) के मौजूद होने की पुष्टि करने के लिए किया जाता है. इसके बाद, चाइल्ड रिकॉर्ड (जैसे, समीक्षा) को सिंगल डेटाबेस ट्रांज़ैक्शन में डाला जाता है.
mutations.gql:
mutation CreateMovieCTE($movieId: UUID!, $userId: UUID!, $reviewId: UUID!) {
_execute(
sql: """
WITH
new_user AS (
INSERT INTO "user" (id, username)
VALUES ($2, 'Auto-Generated User')
ON CONFLICT (id) DO NOTHING
RETURNING id
),
movie AS (
INSERT INTO movie (id, title, image_url, release_year, genre)
VALUES ($1, 'Auto-Generated Movie', 'https://placeholder.com', 2025, 'Sci-Fi')
ON CONFLICT (id) DO NOTHING
RETURNING id
)
INSERT INTO "Reviews" (id, movie_id, user_id, rating, review_text, review_date)
VALUES (
$3,
$1,
$2,
5,
'Good!',
NOW()
)
""",
params: [$movieId, $userId, $reviewId]
)
}
_executeReturning और _executeReturningFirst आपकी क्वेरी को पैरंट सीटीई
में रैप करते हैं, ताकि आउटपुट को JSON के तौर पर फ़ॉर्मैट किया जा सके. PostgreSQL, डेटा में बदलाव करने वाले सीटीई को डेटा में बदलाव करने वाले दूसरे स्टेटमेंट में नेस्ट करने की अनुमति नहीं देता. इस वजह से, क्वेरी में गड़बड़ी होती है.
सातवां उदाहरण: Postgres एक्सटेंशन का इस्तेमाल करना
नेटिव SQL की मदद से, Postgres एक्सटेंशन का इस्तेमाल किया जा सकता है. जैसे, PostGIS. इसके लिए, GraphQL स्कीमा में जटिल ज्यामिति टाइप को मैप करने या अपनी बुनियादी टेबल में बदलाव करने की ज़रूरत नहीं होती.
इस उदाहरण में, मान लें कि आपके रेस्टोरेंट ऐप्लिकेशन में एक टेबल है. इसमें, मेटाडेटा JSON कॉलम में जगह की जानकारी सेव की जाती है. जैसे, {"latitude": 37.3688, "longitude": -122.0363}. अगर आपने PostGIS एक्सटेंशन चालू किया है, तो Postgres के स्टैंडर्ड JSON ऑपरेटर (->>) का इस्तेमाल करके, इन वैल्यू को तुरंत एक्सट्रैक्ट किया जा सकता है. इसके बाद, इन्हें PostGIS ST_MakePoint फ़ंक्शन में पास किया जा सकता है.
query GetNearbyActiveRestaurants($userLong: Float!, $userLat: Float!, $maxDistanceMeters: Float!) @auth(level: USER) {
nearby: _select(
sql: """
SELECT
id,
name,
tags,
ST_Distance(
ST_MakePoint((metadata->>'longitude')::float, (metadata->>'latitude')::float)::geography,
ST_MakePoint($1, $2)::geography
) as distance_meters
FROM restaurant
WHERE active = true
AND metadata ? 'longitude' AND metadata ? 'latitude'
AND ST_DWithin(
ST_MakePoint((metadata->>'longitude')::float, (metadata->>'latitude')::float)::geography,
ST_MakePoint($1, $2)::geography,
$3
)
ORDER BY distance_meters ASC
LIMIT 10
""",
params: [$userLong, $userLat, $maxDistanceMeters]
)
}
क्लाइंट एसडीके का इस्तेमाल करके क्वेरी चलाने के बाद, नतीजा data.nearby में दिखेगा.
सुरक्षा के सबसे सही तरीके: डाइनैमिक SQL और सेव किए गए प्रोसीज़र
Data Connect GraphQL-से-डेटाबेस बाउंड्री पर सभी इनपुट को सुरक्षित तरीके से पैरामीटर में बदलता है. इससे, स्टैंडर्ड SQL क्वेरी को फ़र्स्ट-ऑर्डर SQL इंजेक्शन से पूरी तरह सुरक्षित रखा जा सकता है. हालांकि, अगर SQL का इस्तेमाल करके, Postgres के कस्टम सेव किए गए प्रोसीज़र या फ़ंक्शन को कॉल किया जाता है, जो डाइनैमिक SQL को एक्ज़ीक्यूट करते हैं, तो आपको यह पक्का करना होगा कि आपका इंटरनल PL/pgSQL कोड, इन पैरामीटर को सुरक्षित तरीके से हैंडल करे.
अगर आपका सेव किया गया प्रोसीज़र, उपयोगकर्ता के इनपुट को सीधे तौर पर EXECUTE स्ट्रिंग में जोड़ता है, तो यह पैरामीटर में बदलने की प्रोसेस को बायपास कर देता है. साथ ही, सेकंड-ऑर्डर SQL इंजेक्शन की समस्या पैदा करता है:
-- INSECURE: Do not concatenate parameters into dynamic strings!
CREATE OR REPLACE PROCEDURE unsafe_update(user_input TEXT)
LANGUAGE plpgsql AS $$
BEGIN
-- A malicious user_input (e.g., "val'; DROP TABLE users; --") will execute as code.
EXECUTE 'UPDATE target_table SET status = ''' || user_input || '''';
END;
$$;
इससे बचने के लिए, इन सबसे सही तरीकों को अपनाएं:
USINGक्लॉज़ का इस्तेमाल करें: सेव किए गए प्रोसीज़र में डाइनैमिक SQL लिखते समय, डेटा पैरामीटर को सुरक्षित तरीके से बाइंड करने के लिए हमेशाUSINGक्लॉज़ का इस्तेमाल करें.- आइडेंटिफ़ायर के लिए
format()का इस्तेमाल करें: डेटाबेस आइडेंटिफ़ायर (जैसे, टेबल के नाम) को सुरक्षित तरीके से इंजेक्ट करने के लिए,%Iफ़्लैग के साथformat()का इस्तेमाल करें. - आइडेंटिफ़ायर को अनुमति दें: क्लाइंट ऐप्लिकेशन को डेटाबेस आइडेंटिफ़ायर चुनने की अनुमति न दें. अगर आपके प्रोसीज़र के लिए डाइनैमिक आइडेंटिफ़ायर की ज़रूरत है, तो एक्ज़ीक्यूशन से पहले, अपने PL/pgSQL लॉजिक में हार्डकोड की गई अनुमति वाली सूची के हिसाब से इनपुट की पुष्टि करें.
-- SECURE: Use format() for identifiers and USING for data values
CREATE OR REPLACE PROCEDURE secure_update(target_table TEXT, new_value TEXT, row_id INT)
LANGUAGE plpgsql AS $$
BEGIN
-- Validate the dynamic table name against an allowlist
IF target_table NOT IN ('orders', 'users', 'inventory') THEN
RAISE EXCEPTION 'Invalid table name';
END IF;
-- Execute securely
EXECUTE format('UPDATE %I SET status = $1 WHERE id = $2', target_table)
USING new_value, row_id;
END;
$$;