GraphQL के बजाय, एसक्यूएल का इस्तेमाल करके Firebase SQL Connect के ऑपरेशन लिखने के बारे में जानकारी. page_type: guide announcement: > Native SQL is available as a feature Preview, which means that it isn't subject to any SLA or deprecation policy and could change in backwards-incompatible ways. अगर इस सुविधा का इस्तेमाल, सेव की गई उन प्रक्रियाओं या फ़ंक्शन के साथ किया जाता है जो डाइनैमिक एसक्यूएल को एक्ज़ीक्यूट करते हैं, तो इस पेज के सबसे नीचे बताए गए सुरक्षा के सबसे सही तरीकों को अपनाएं.
Firebase SQL Connect आपके Cloud SQL डेटाबेस के साथ इंटरैक्ट करने के कई तरीके उपलब्ध कराता है:
- Native GraphQL: अपने
schema.gqlमें टाइप तय करें. इसके बाद, SQL Connect GraphQL के ऑपरेशन को एसक्यूएल में बदलता है. यह स्टैंडर्ड तरीका है. इसमें टाइपिंग और स्कीमा-एनफ़ोर्स्ड स्ट्रक्चर की सुविधा मिलती है. इस पेज के अलावा, SQL Connect के ज़्यादातर दस्तावेज़ों में इस विकल्प के बारे में बताया गया है. अगर मुमकिन हो, तो इस तरीके का इस्तेमाल करें, ताकि टाइप की पूरी सुरक्षा और टूलिंग की सुविधा का फ़ायदा मिल सके. - The
@viewdirective: कस्टमSELECTएसक्यूएल स्टेटमेंट के आधार पर,schema.gqlमें GraphQL टाइप तय करें. यह सिर्फ़ पढ़ने के लिए, मज़बूत टाइप वाले व्यू बनाने के लिए काम का है. यह जटिल एसक्यूएल लॉजिक पर आधारित होता है. इन टाइप के लिए, सामान्य टाइप की तरह क्वेरी की जा सकती है.@viewदेखें. - Native SQL: खास रूट फ़ील्ड का इस्तेमाल करके,
.gqlफ़ाइलों में नाम वाले ऑपरेशन में सीधे तौर पर एसक्यूएल स्टेटमेंट एम्बेड करें. इससे ज़्यादा फ़्लेक्सिबिलिटी और डायरेक्ट कंट्रोल मिलता है. खास तौर पर, उन ऑपरेशन के लिए जो स्टैंडर्ड GraphQL के साथ काम नहीं करते. साथ ही, डेटाबेस से जुड़ी सुविधाओं का इस्तेमाल करने या PostgreSQL एक्सटेंशन का इस्तेमाल करने के लिए भी यह तरीका काम का है. GraphQL और@viewडायरेक्टिव के उलट, नेटिव एसक्यूएल, मज़बूत टाइप वाला आउटपुट नहीं देता.
इस गाइड में, Native SQL विकल्प पर फ़ोकस किया गया है.
नेटिव एसक्यूएल के इस्तेमाल के सामान्य उदाहरण
नेटिव GraphQL में टाइप की पूरी सुरक्षा मिलती है. वहीं, @view डायरेक्टिव, सिर्फ़ पढ़ने के लिए एसक्यूएल रिपोर्ट के लिए मज़बूत टाइप वाले नतीजे देता है. इसके अलावा, नेटिव एसक्यूएल, इन कामों के लिए ज़रूरी फ़्लेक्सिबिलिटी देता है:
- PostgreSQL एक्सटेंशन: अपने GraphQL स्कीमा में जटिल टाइप को मैप किए बिना, इंस्टॉल किए गए किसी भी PostgreSQL
एक्सटेंशन (जैसे, जियोस्पेशल डेटा के लिए
PostGIS) के लिए सीधे तौर पर क्वेरी करें और उसका इस्तेमाल करें. - जटिल क्वेरी: जॉइन, सबक्वेरी, एग्रीगेशन, विंडो फ़ंक्शन, और सेव की गई प्रक्रियाओं के साथ जटिल एसक्यूएल को एक्ज़ीक्यूट करें.
- डेटा मैनिप्युलेशन (डीएमएल):
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 Unicode एस्केप (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" और
"profileId" कॉलम के लिए userId का इस्तेमाल करना होगा. displayName फ़ील्ड, डिफ़ॉल्ट रूप से display_name में बदल जाता है:
schema.gql |
queries.gql |
|---|---|
|
|
इस्तेमाल करने के उदाहरण
पहला उदाहरण: फ़ील्ड एलियासिंग के साथ सामान्य SELECT
क्लाइंट रिस्पॉन्स को साफ़-सुथरा बनाने के लिए, रूट फ़ील्ड का एलियास बनाया जा सकता है. उदाहरण के लिए, movies: _select के बजाय data.movies का इस्तेमाल किया जा सकता है.data._select
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 और Auth कॉन्टेक्स्ट के साथ 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, डेटा में बदलाव करने वाले सीटीई को डेटा में बदलाव करने वाले दूसरे स्टेटमेंट में नेस्ट करने की अनुमति नहीं देता. इससे क्वेरी काम नहीं करती.
सातवां उदाहरण: 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]
)
}
क्लाइंट एसडीके का इस्तेमाल करके क्वेरी चलाने के बाद, नतीजा data.nearby में दिखेगा.
सुरक्षा के सबसे सही तरीके: डाइनैमिक एसक्यूएल और सेव की गई प्रक्रियाएं
SQL Connect GraphQL-से-डेटाबेस बाउंड्री पर सभी इनपुट को सुरक्षित तरीके से पैरामीटर में बदलता है. इससे, स्टैंडर्ड एसक्यूएल क्वेरी को पहले ऑर्डर के एसक्यूएल इंजेक्शन से पूरी तरह सुरक्षित रखा जा सकता है. हालांकि, अगर एसक्यूएल का इस्तेमाल करके, 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;
$$;