GraphQL-এর পরিবর্তে SQL ব্যবহার করে Firebase SQL Connect অপারেশন লেখার একটি নির্দেশিকা। পৃষ্ঠা প্রকার: নির্দেশিকা ঘোষণা: > নেটিভ SQL একটি ফিচার প্রিভিউ হিসেবে উপলব্ধ, যার অর্থ হলো এটি কোনো SLA বা ডেপ্রিকেশন পলিসির অধীন নয় এবং ব্যাকওয়ার্ড-ইনকমপ্যাটিবল উপায়ে পরিবর্তিত হতে পারে। আপনি যদি এই ফিচারটি স্টোরড প্রসিডিউর বা ফাংশনের সাথে ব্যবহার করেন যা ডাইনামিক SQL এক্সিকিউট করে, তাহলে এই পৃষ্ঠার নীচে ব্যাখ্যা করা নিরাপত্তা সংক্রান্ত সেরা অনুশীলনগুলি অনুসরণ করুন।
Firebase SQL Connect আপনার Cloud SQL ডেটাবেসের সাথে যোগাযোগ করার একাধিক উপায় প্রদান করে:
- নেটিভ গ্রাফকিউএল (Native GraphQL) : আপনার
schema.gqlটাইপগুলো সংজ্ঞায়িত করুন এবং SQL Connect আপনার গ্রাফকিউএল অপারেশনগুলোকে SQL-এ অনুবাদ করবে। এটিই আদর্শ পদ্ধতি, যা শক্তিশালী টাইপিং এবং স্কিমা-দ্বারা-বাধ্যতামূলক কাঠামো প্রদান করে। এই পৃষ্ঠাটি ছাড়া SQL Connect এর বেশিরভাগ ডকুমেন্টেশনে এই বিকল্পটি নিয়ে আলোচনা করা হয়েছে। যখন সম্ভব, সম্পূর্ণ টাইপ সুরক্ষা এবং টুলিং সাপোর্টের সুবিধা নিতে আপনার এই পদ্ধতিটি ব্যবহার করা উচিত। -
@viewডিরেক্টিভ :schema.gqlএ একটি কাস্টমSELECTSQL স্টেটমেন্ট দ্বারা সমর্থিত একটি GraphQL টাইপ সংজ্ঞায়িত করুন। জটিল SQL লজিকের উপর ভিত্তি করে রিড-অনলি, স্ট্রংলি-টাইপড ভিউ তৈরি করার জন্য এটি উপযোগী। এই টাইপগুলো সাধারণ টাইপের মতোই কোয়েরিযোগ্য।@viewদেখুন। - নেটিভ SQL : বিশেষ রুট ফিল্ড ব্যবহার করে
.gqlফাইলের নামযুক্ত অপারেশনগুলিতে সরাসরি SQL স্টেটমেন্ট এম্বেড করুন। এটি সর্বোচ্চ নমনীয়তা এবং সরাসরি নিয়ন্ত্রণ প্রদান করে, বিশেষ করে সেইসব অপারেশনের জন্য যা স্ট্যান্ডার্ড GraphQL দ্বারা সমর্থিত নয়, ডাটাবেস-নির্দিষ্ট বৈশিষ্ট্য ব্যবহার করে, অথবা PostgreSQL এক্সটেনশন কাজে লাগায়। GraphQL এবং@viewডিরেক্টিভের মতো নয়, নেটিভ SQL স্ট্রংলি-টাইপড আউটপুট প্রদান করে না।
এই নির্দেশিকাটি নেটিভ SQL বিকল্পটির উপর আলোকপাত করে।
নেটিভ SQL এর সাধারণ ব্যবহারের ক্ষেত্র
যদিও নেটিভ GraphQL সম্পূর্ণ টাইপ সেফটি প্রদান করে এবং @view ডিরেক্টিভটি রিড-অনলি SQL রিপোর্টের জন্য স্ট্রংলি-টাইপড ফলাফল দেয়, নেটিভ SQL নিম্নলিখিত ক্ষেত্রগুলির জন্য প্রয়োজনীয় নমনীয়তা প্রদান করে:
- PostgreSQL এক্সটেনশন : আপনার GraphQL স্কিমাতে জটিল টাইপ ম্যাপ করার প্রয়োজন ছাড়াই, ইনস্টল করা যেকোনো PostgreSQL এক্সটেনশন (যেমন ভূ-স্থানিক ডেটার জন্য
PostGIS) সরাসরি কোয়েরি করুন এবং ব্যবহার করুন। - জটিল কোয়েরি : জয়েন, সাবকোয়েরি, অ্যাগ্রিগেশন, উইন্ডো ফাংশন এবং স্টোরড প্রসিডিউর ব্যবহার করে জটিল SQL সম্পাদন করুন।
- ডেটা ম্যানিপুলেশন (DML) : সরাসরি
INSERT, UPDATE, DELETEঅপারেশন সম্পাদন করুন। (তবে, ডেটা ডেফিনিশন ল্যাঙ্গুয়েজ (DDL) কমান্ডের জন্য নেটিভ SQL ব্যবহার করবেন না। আপনার ব্যাকএন্ড এবং জেনারেটেড SDK-গুলোকে সিঙ্কে রাখতে আপনাকে অবশ্যই GraphQL ব্যবহার করে স্কিমা-স্তরের পরিবর্তন করতে হবে।) - ডাটাবেস-নির্দিষ্ট বৈশিষ্ট্য : PostgreSQL-এর নিজস্ব ফাংশন, অপারেটর বা ডেটা টাইপ ব্যবহার করুন।
- পারফরম্যান্স অপ্টিমাইজেশন : ক্রিটিক্যাল পাথগুলোর জন্য SQL স্টেটমেন্টগুলো ম্যানুয়ালি টিউন করুন।
নেটিভ SQL রুট ফিল্ড
SQL-এ অপারেশন লেখার জন্য, query বা mutation টাইপের নিম্নলিখিত রুট ফিল্ডগুলির মধ্যে একটি ব্যবহার করুন:
query ক্ষেত্রগুলি
| মাঠ | বর্ণনা |
|---|---|
_select | এমন একটি SQL কোয়েরি নির্বাহ করে যা শূন্য বা তার অধিক সংখ্যক সারি ফেরত দেয়। যুক্তি :
ফেরত দেয় : একটি JSON অ্যারে ( |
_selectFirst | এমন একটি SQL কোয়েরি নির্বাহ করে যা থেকে শূন্য বা একটি সারি ফেরত আসার কথা। যুক্তি :
ফেরত দেয় : একটি JSON অবজেক্ট ( |
mutation ক্ষেত্র
| মাঠ | বর্ণনা |
|---|---|
_execute | একটি DML স্টেটমেন্ট ( যুক্তি :
রিটার্ন করে : একটি ফলাফলে |
_executeReturning | যুক্তি :
ফেরত দেয় : একটি JSON অ্যারে ( |
_executeReturningFirst | যুক্তি :
ফেরত দেয় : একটি JSON অবজেক্ট ( |
নোট:
- SQL Connect সার্ভিস অ্যাকাউন্টকে প্রদত্ত অনুমতি ব্যবহার করে অপারেশনগুলো সম্পাদন করা হয়।
সিনট্যাক্স নিয়ম ও সীমাবদ্ধতা
নিরাপত্তা নিশ্চিত করতে এবং SQL ইনজেকশন প্রতিরোধ করতে নেটিভ SQL কঠোর পার্সিং নিয়ম প্রয়োগ করে। নিম্নলিখিত সীমাবদ্ধতাগুলো সম্পর্কে সচেতন থাকুন:
- মন্তব্য : ব্লক কমেন্ট (
/* ... */) ব্যবহার করুন। লাইন কমেন্ট (--) ব্যবহার করা নিষিদ্ধ, কারণ কোয়েরি সংযুক্ত করার সময় এগুলি পরবর্তী ক্লজগুলিকে (যেমন সিকিউরিটি ফিল্টার) কেটে ফেলতে পারে। - প্যারামিটার :
paramsঅ্যারের ক্রম অনুসারে পজিশনাল প্যারামিটার ($1,$2) ব্যবহার করুন। নেমড প্যারামিটার ($id,:name) সমর্থিত নয়। - স্ট্রিং : বর্ধিত স্ট্রিং লিটারেল (
E'...') এবং ডলার-কোটেড স্ট্রিং ($$...$$) সমর্থিত। PostgreSQL ইউনিকোড এস্কেপ (U&'...') সমর্থিত নয়।
মন্তব্যে প্যারামিটার
পার্সার ব্লক কমেন্টের ভেতরের সবকিছু উপেক্ষা করে। যদি আপনি কোনো প্যারামিটারযুক্ত লাইন কমেন্ট করে দেন (উদাহরণস্বরূপ, /* WHERE id = $1 */ ), তাহলে আপনাকে অবশ্যই params তালিকা থেকেও সেই প্যারামিটারটি সরিয়ে ফেলতে হবে, নতুবা অপারেশনটি unused parameter: $1 ত্রুটিসহ ব্যর্থ হবে।
নামকরণের নিয়মাবলী
নেটিভ SQL লেখার সময়, আপনি সরাসরি আপনার PostgreSQL ডাটাবেসের সাথে ইন্টারঅ্যাক্ট করেন, তাই টেবিল এবং কলামের জন্য আপনাকে অবশ্যই আসল ডাটাবেসের নাম ব্যবহার করতে হবে। ডিফল্টরূপে, SQL Connect স্বয়ংক্রিয়ভাবে আপনার GraphQL স্কিমার নামগুলিকে ডাটাবেসের স্নেক কেসে ম্যাপ করে, যদি না আপনি @table(name) এবং @col(name) নির্দেশিকা ব্যবহার করে PostgreSQL আইডেন্টিফায়ারগুলিকে স্পষ্টভাবে কাস্টমাইজ করেন।
আপনি যদি নির্দেশিকা ছাড়া কোনো টাইপ সংজ্ঞায়িত করেন, তাহলে GraphQL টেবিল এবং ফিল্ডের নামগুলো ডিফল্ট snake_case PostgreSQL আইডেন্টিফায়ারে ম্যাপ হয়:
schema.gql | queries.gql |
|---|---|
| |
PostgreSQL আইডেন্টিফায়ারগুলো ডিফল্টরূপে কেস-ইনসেনসিটিভ হয়। যদি আপনি @table বা @col মতো নির্দেশিকা ব্যবহার করে এমন কোনো নাম নির্দিষ্ট করেন যাতে বড় হাতের বা মিশ্র-কেস অক্ষর থাকে, তাহলে আপনার SQL স্টেটমেন্টে সেই আইডেন্টিফায়ারটিকে অবশ্যই ডাবল কোটেশনের মধ্যে রাখতে হবে।
নিম্নলিখিত উদাহরণে, আপনাকে টেবিলের নাম হিসেবে অবশ্যই "UserProfiles" এবং userId কলামের জন্য "profileId" ব্যবহার করতে হবে। displayName ফিল্ডটি ডিফল্টভাবে display_name এ রূপান্তরিত হয়:
schema.gql | queries.gql |
|---|---|
| |
ব্যবহারের উদাহরণ
উদাহরণ ১: ফিল্ড অ্যালিয়াসিং সহ মৌলিক SELECT
ক্লায়েন্টের প্রতিক্রিয়া আরও পরিচ্ছন্ন করার জন্য ( data.movies এর পরিবর্তে data._select ) আপনি রুট ফিল্ডের একটি অ্যালিয়াস (যেমন, movies: _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]
)
}
ক্লায়েন্ট SDK ব্যবহার করে কোয়েরিটি চালানোর পর, ফলাফলটি data.movies ফাইলে পাওয়া যাবে।
উদাহরণ ২: মৌলিক আপডেট
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 ফাইলে পাওয়া যাবে।
উদাহরণ ৪: 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"}]
)
}
ক্লায়েন্ট SDK ব্যবহার করে মিউটেশনটি চালানোর পর, আপডেট করা পোস্ট ডেটা data.updatedReview -এ থাকবে।
উদাহরণ ৬: আপসার্ট সহ উন্নত CTE (অ্যাটোমিক গেট-অর-ক্রিয়েট)
এই প্যাটার্নটি একটিমাত্র ডাটাবেস ট্রানজ্যাকশনের মধ্যে কোনো চাইল্ড রেকর্ড (যেমন রিভিউ) ইনসার্ট করার আগে ডিপেন্ডেন্ট রেকর্ড (যেমন ইউজার বা মুভি) বিদ্যমান আছে কিনা তা নিশ্চিত করতে সহায়ক।
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]
)
}
আউটপুটকে JSON ফরম্যাটে রূপান্তর করতে _executeReturning এবং _executeReturningFirst আপনার কোয়েরিকে একটি প্যারেন্ট CTE-এর মধ্যে আবদ্ধ করে। PostgreSQL একটি ডেটা-পরিবর্তনকারী স্টেটমেন্টের ভিতরে আরেকটি ডেটা-পরিবর্তনকারী CTE নেস্ট করার অনুমতি দেয় না, যার ফলে কোয়েরিটি ব্যর্থ হয়।
উদাহরণ ৭: PostgreSQL এক্সটেনশন ব্যবহার
নেটিভ SQL আপনাকে আপনার GraphQL স্কিমাতে জটিল জ্যামিতি টাইপ ম্যাপ করা বা আপনার অন্তর্নিহিত টেবিলগুলি পরিবর্তন করার প্রয়োজন ছাড়াই PostGIS-এর মতো PostgreSQL এক্সটেনশনগুলি ব্যবহার করার সুযোগ দেয়।
এই উদাহরণে, ধরুন আপনার রেস্তোরাঁ অ্যাপে একটি টেবিল আছে যা একটি মেটাডেটা 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 এবং স্টোরড প্রসিডিউর
SQL Connect GraphQL ও ডাটাবেসের সংযোগস্থলে সমস্ত ইনপুটকে নিরাপদে প্যারামিটারাইজ করে, যা আপনার সাধারণ SQL কোয়েরিগুলোকে ফার্স্ট-অর্ডার SQL ইনজেকশন থেকে সম্পূর্ণরূপে সুরক্ষিত রাখে। তবে, আপনি যদি কাস্টম PostgreSQL স্টোরড প্রসিডিউর বা ডাইনামিক SQL এক্সিকিউট করে এমন ফাংশন কল করার জন্য 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 লজিকের ভিতরে একটি হার্ডকোডেড অনুমোদিত তালিকার (allowlist) সাথে ইনপুটটি যাচাই করে নিন।
-- 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;
$$;