एसक्यूएल के बजाय एसक्यूएल का इस्तेमाल करके, Firebase SQL Connect ऑपरेशन लिखने के लिए गाइड. page_type: guide announcement: > नेटिव एसक्यूएल, सुविधा की झलक के तौर पर उपलब्ध है. इसका मतलब है कि यह किसी भी एसएलए या समर्थन नहीं होने की नीति के तहत नहीं आता है. साथ ही, यह पीछे की ओर काम न करने वाले तरीकों से बदल सकता है. अगर आपको इस सुविधा का इस्तेमाल, सेव किए गए ऐसे प्रोसीज़र या फ़ंक्शन के साथ करना है जो डाइनैमिक एसक्यूएल को एक्ज़ीक्यूट करते हैं, तो सुरक्षा से जुड़े सबसे सही तरीकों को अपनाएं. इनके बारे में इस पेज के सबसे नीचे बताया गया है.
Firebase SQL Connect की मदद से, Cloud SQL डेटाबेस के साथ कई तरीकों से इंटरैक्ट किया जा सकता है:
- नेटिव GraphQL: अपने
schema.gqlमें टाइप तय करें. साथ ही, SQL Connect आपके GraphQL ऑपरेशन को एसक्यूएल में बदलता है. यह एक स्टैंडर्ड तरीका है. इसमें टाइपिंग और स्कीमा लागू करने वाले स्ट्रक्चर की सुविधा मिलती है. इस पेज के अलावा, SQL Connect के ज़्यादातर दस्तावेज़ों में इस विकल्प के बारे में बताया गया है. जब भी हो सके, आपको इस तरीके का इस्तेमाल करना चाहिए, ताकि टाइप की पूरी सुरक्षा और टूलिंग सपोर्ट का फ़ायदा मिल सके. @viewडायरेक्टिव:schema.gqlमें GraphQL टाइप तय करें. यह कस्टमSELECTएसक्यूएल स्टेटमेंट पर आधारित होता है. यह जटिल एसक्यूएल लॉजिक पर आधारित, सिर्फ़ पढ़ने के लिए और टाइप किए गए व्यू बनाने के लिए काम आता है. इन टाइप को सामान्य टाइप की तरह क्वेरी किया जा सकता है.@viewदेखें.- नेटिव एसक्यूएल: खास रूट फ़ील्ड का इस्तेमाल करके,
.gqlफ़ाइलों में सीधे तौर पर एसक्यूएल स्टेटमेंट एम्बेड करें. इससे ज़्यादा से ज़्यादा फ़्लेक्सिबिलिटी और सीधा कंट्रोल मिलता है. खास तौर पर, उन कार्रवाइयों के लिए जिन्हें स्टैंडर्ड GraphQL के साथ इस्तेमाल नहीं किया जा सकता. जैसे, डेटाबेस से जुड़ी सुविधाओं का फ़ायदा उठाना या PostgreSQL एक्सटेंशन का इस्तेमाल करना. GraphQL और@viewडायरेक्टिव के उलट, नेटिव एसक्यूएल से टाइप किया गया आउटपुट नहीं मिलता.
इस गाइड में, नेटिव एसक्यूएल विकल्प पर फ़ोकस किया गया है.
नेटिव एसक्यूएल के इस्तेमाल के सामान्य उदाहरण
नेटिव GraphQL, टाइप की पूरी सुरक्षा देता है. साथ ही, @view डायरेक्टिव, सिर्फ़ पढ़ने के लिए उपलब्ध एसक्यूएल रिपोर्ट के लिए, टाइप किए गए नतीजे देता है. वहीं, नेटिव एसक्यूएल इन कामों के लिए ज़रूरी फ़्लेक्सिबिलिटी देता है:
- PostgreSQL एक्सटेंशन: इंस्टॉल किए गए किसी भी PostgreSQL एक्सटेंशन (जैसे, जियोस्पेशल डेटा के लिए
PostGIS) को सीधे तौर पर क्वेरी किया जा सकता है और इस्तेमाल किया जा सकता है. इसके लिए, आपको अपने GraphQL स्कीमा में जटिल टाइप को मैप करने की ज़रूरत नहीं होती. - जटिल क्वेरी: जॉइन, सबक्वेरी, एग्रीगेशन, विंडो फ़ंक्शन, और सेव की गई प्रक्रियाओं के साथ जटिल एसक्यूएल को लागू करें.
- डेटा मैनिपुलेशन (डीएमएल): सीधे तौर पर
INSERT, UPDATE, DELETEकार्रवाइयां करें. (हालांकि, डेटा डेफ़िनिशन लैंग्वेज (डीडीएल) कमांड के लिए नेटिव एसक्यूएल का इस्तेमाल न करें. आपको स्कीमा लेवल पर बदलाव करने के लिए, GraphQL का इस्तेमाल जारी रखना होगा, ताकि आपका बैकएंड और जनरेट किए गए एसडीके सिंक रहें.) - डेटाबेस के हिसाब से उपलब्ध सुविधाएं: PostgreSQL के लिए खास तौर पर उपलब्ध फ़ंक्शन, ऑपरेटर या डेटा टाइप का इस्तेमाल करें.
- परफ़ॉर्मेंस ऑप्टिमाइज़ेशन: अहम पाथ के लिए, एसक्यूएल स्टेटमेंट को मैन्युअल तरीके से ट्यून करें.
नेटिव एसक्यूएल रूट फ़ील्ड
एसक्यूएल की मदद से डेटा लिखने के लिए, query या mutation टाइप के इन रूट फ़ील्ड में से किसी एक का इस्तेमाल करें:
query फ़ील्ड
| फ़ील्ड | ब्यौरा |
|---|---|
_select |
यह फ़ंक्शन, एसक्यूएल क्वेरी को लागू करता है. इससे शून्य या उससे ज़्यादा लाइनें मिलती हैं. आर्ग्युमेंट:
जवाब: JSON कलेक्शन ( |
_selectFirst |
यह फ़ंक्शन, ऐसी एसक्यूएल क्वेरी को लागू करता है जिससे शून्य या एक लाइन मिलती है. आर्ग्युमेंट:
वैल्यू दिखाता है: एक JSON ऑब्जेक्ट ( |
mutation फ़ील्ड
| फ़ील्ड | ब्यौरा |
|---|---|
_execute |
डीएमएल स्टेटमेंट ( आर्ग्युमेंट:
वैल्यू दिखाता है: नतीजे में |
_executeReturning |
यह फ़ंक्शन, आर्ग्युमेंट:
जवाब: JSON कलेक्शन ( |
_executeReturningFirst |
यह फ़ंक्शन, आर्ग्युमेंट:
वैल्यू दिखाता है: एक JSON ऑब्जेक्ट ( |
ध्यान दें:
- कार्रवाइयां, SQL Connect सेवा खाते को दी गई अनुमतियों का इस्तेमाल करके की जाती हैं.
सिंटैक्स के नियम और सीमाएं
नेटिव एसक्यूएल में पार्स करने के सख्त नियम लागू किए जाते हैं, ताकि सुरक्षा को पक्का किया जा सके और एसक्यूएल इंजेक्शन को रोका जा सके. इन बातों का ध्यान रखें:
- टिप्पणियां: ब्लॉक टिप्पणियों (
/* ... */) का इस्तेमाल करें. लाइन टिप्पणियों (--) का इस्तेमाल करने की अनुमति नहीं है, क्योंकि क्वेरी को एक साथ जोड़ते समय, ये बाद के क्लॉज़ (जैसे कि सुरक्षा फ़िल्टर) को छोटा कर सकती हैं. - पैरामीटर: पोज़िशनल पैरामीटर (
$1,$2) का इस्तेमाल करें. ये पैरामीटर,paramsऐरे के क्रम से मेल खाने चाहिए. नाम वाले पैरामीटर ($id,:name) मौजूद नहीं हैं. - स्ट्रिंग: एक्सटेंड की गई स्ट्रिंग लिटरल (
E'...') और डॉलर-कोट की गई स्ट्रिंग ($$...$$) इस्तेमाल की जा सकती हैं. PostgreSQL यूनिकोड एस्केप (U&'...') काम नहीं करते.
टिप्पणियों में पैरामीटर
पार्सर, ब्लॉक की गई टिप्पणी में मौजूद सभी चीज़ों को अनदेखा करता है. अगर आपको किसी पैरामीटर (उदाहरण के लिए, /* WHERE id = $1 */) वाली लाइन को कमेंट आउट करना है, तो आपको उस पैरामीटर को params सूची से भी हटाना होगा. ऐसा न करने पर, ऑपरेशन पूरा नहीं होगा और आपको गड़बड़ी unused parameter: $1 दिखेगी.
नाम रखने के नियम
नेटिव एसक्यूएल लिखते समय, सीधे तौर पर अपने PostgreSQL डेटाबेस से इंटरैक्ट किया जाता है. इसलिए, आपको टेबल और कॉलम के लिए डेटाबेस के असली नाम इस्तेमाल करने होंगे. डिफ़ॉल्ट रूप से, SQL Connect आपके GraphQL स्कीमा में मौजूद नामों को डेटाबेस में स्नेक केस में अपने-आप मैप कर देता है. हालांकि, @table(name) और @col(name) डायरेक्टिव का इस्तेमाल करके, PostgreSQL आइडेंटिफ़ायर को अपनी पसंद के मुताबिक बनाया जा सकता है.
अगर आपने बिना डायरेक्टिव के कोई टाइप तय किया है, तो GraphQL टेबल और फ़ील्ड के नाम, डिफ़ॉल्ट snake_case PostgreSQL आइडेंटिफ़ायर पर मैप करते हैं:
schema.gql |
queries.gql |
|---|---|
|
|
PostgreSQL आइडेंटिफ़ायर डिफ़ॉल्ट रूप से केस-इनसेंसिटिव होते हैं. अगर आपको अपरकेस या मिक्स-केस वाले अक्षरों से बने नाम के लिए, @table या @col जैसे डायरेक्टिव इस्तेमाल करने हैं, तो आपको अपने एसक्यूएल स्टेटमेंट में उस आइडेंटिफ़ायर को डबल कोट में रखना ज़रूरी है.
इस उदाहरण में, आपको टेबल के नाम के लिए "UserProfiles" और userId कॉलम के लिए "profileId" का इस्तेमाल करना होगा. displayName फ़ील्ड, डिफ़ॉल्ट रूप से display_name में बदल जाता है:
schema.gql |
queries.gql |
|---|---|
|
|
इस्तेमाल करने के उदाहरण
पहला उदाहरण: फ़ील्ड के लिए अन्य नाम का इस्तेमाल करके 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]
)
}
क्लाइंट SDK का इस्तेमाल करके क्वेरी चलाने के बाद, नतीजा data.movies में दिखेगा.
उदाहरण 2: सामान्य 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]
)
}
क्लाइंट SDK टूल का इस्तेमाल करके म्यूटेशन चलाने के बाद, असर वाली पंक्तियों की संख्या data._execute में होगी.
तीसरा उदाहरण: बुनियादी एग्रीगेशन
queries.gql:
query GetTotalReviewCount @auth(level: PUBLIC) {
stats: _selectFirst(
sql: "SELECT COUNT(*) as total_reviews FROM \"Reviews\""
)
}
क्लाइंट SDK का इस्तेमाल करके क्वेरी चलाने के बाद, नतीजा data.stats.total_reviews में दिखेगा.
उदाहरण 4: 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: []
)
}
क्लाइंट SDK का इस्तेमाल करके क्वेरी चलाने के बाद, नतीजा data._select में दिखेगा.
पांचवां उदाहरण: RETURNING और Auth Context के साथ 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!) @auth(level: USER) {
_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 में, डेटा में बदलाव करने वाले CTE को डेटा में बदलाव करने वाले किसी दूसरे स्टेटमेंट में नेस्ट करने की अनुमति नहीं है. इस वजह से, क्वेरी पूरी नहीं हो पाती.
उदाहरण 7: PostgreSQL एक्सटेंशन का इस्तेमाल करना
नेटिव एसक्यूएल की मदद से, PostgreSQL एक्सटेंशन का इस्तेमाल किया जा सकता है. जैसे, PostGIS. इसके लिए, आपको जटिल ज्यामिति टाइप को अपने GraphQL स्कीमा में मैप करने या अपनी बुनियादी टेबल में बदलाव करने की ज़रूरत नहीं होती.
इस उदाहरण में मान लें कि आपके रेस्टोरेंट ऐप्लिकेशन में एक ऐसी टेबल है जो मेटाडेटा JSON कॉलम में जगह की जानकारी का डेटा सेव करती है. उदाहरण के लिए, {"latitude": 37.3688,
"longitude": -122.0363}. अगर आपने PostGIS एक्सटेंशन चालू किया है, तो PostgreSQL के स्टैंडर्ड 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]
)
}
क्लाइंट SDK का इस्तेमाल करके क्वेरी चलाने के बाद, नतीजा data.nearby में दिखेगा.
सुरक्षा के सबसे सही तरीके: डाइनैमिक एसक्यूएल और स्टोर की गई प्रक्रियाएं
SQL Connect GraphQL-to-database बाउंड्री पर सभी इनपुट को सुरक्षित तरीके से पैरामीटर में बदलता है. इससे आपकी स्टैंडर्ड एसक्यूएल क्वेरी, पहले ऑर्डर के एसक्यूएल इंजेक्शन से पूरी तरह सुरक्षित रहती हैं. हालांकि, अगर डाइनैमिक एसक्यूएल को लागू करने वाले कस्टम PostgreSQL के सेव किए गए फ़ंक्शन या प्रोसीज़र को कॉल करने के लिए एसक्यूएल का इस्तेमाल किया जाता है, तो आपको यह पक्का करना होगा कि आपका इंटरनल PL/pgSQL कोड, इन पैरामीटर को सुरक्षित तरीके से हैंडल करता हो.
अगर आपकी सेव की गई प्रोसेस, उपयोगकर्ता के इनपुट को सीधे तौर पर EXECUTE स्ट्रिंग में जोड़ती है, तो यह पैरामीटर के इस्तेमाल को बायपास कर देती है. इससे दूसरे क्रम की एसक्यूएल इंजेक्शन जोखिम की आशंका पैदा होती है:
-- 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क्लॉज़ का इस्तेमाल करें: सेव किए गए अपने प्रोसीज़र में डाइनैमिक एसक्यूएल लिखते समय, डेटा पैरामीटर को सुरक्षित तरीके से बाइंड करने के लिए हमेशा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;
$$;