Firebase Data Connect আপনার ক্লাউড SQL ডেটাবেসের সাথে যোগাযোগ করার একাধিক উপায় প্রদান করে:
- নেটিভ গ্রাফকিউএল (Native GraphQL) : আপনার
schema.gqlফাইলে টাইপগুলো সংজ্ঞায়িত করুন এবং Data Connect আপনার গ্রাফকিউএল অপারেশনগুলোকে এসকিউএল (SQL)-এ অনুবাদ করবে। এটিই আদর্শ পদ্ধতি, যা শক্তিশালী টাইপিং এবং স্কিমা-দ্বারা-বাধ্যতামূলক কাঠামো প্রদান করে। এই পৃষ্ঠাটি ছাড়া Data 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 অবজেক্ট ( |
নোট:
Data Connect সার্ভিস অ্যাকাউন্টে প্রদত্ত অনুমতি ব্যবহার করে অপারেশনগুলো সম্পাদন করা হয়।
যদি আপনি
@tableডিরেক্টিভ (@table(name: "ExampleTable")) ব্যবহার করে টেবিলের নাম স্পষ্টভাবে সেট করেন, তাহলে আপনাকে অবশ্যই আপনার SQL স্টেটমেন্টগুলিতে (SELECT field FROM "ExampleTable" ...) টেবিলের নামটি উদ্ধৃতি চিহ্নের মধ্যে রাখতে হবে।উদ্ধৃতি চিহ্ন ছাড়া, Data Connect টেবিলের নামটি স্নেক কেসে (
example_table) রূপান্তর করবে।
সিনট্যাক্স নিয়ম ও সীমাবদ্ধতা
নিরাপত্তা নিশ্চিত করতে এবং SQL ইনজেকশন প্রতিরোধ করতে নেটিভ SQL কঠোর পার্সিং নিয়ম প্রয়োগ করে। নিম্নলিখিত সীমাবদ্ধতাগুলো সম্পর্কে সচেতন থাকুন:
- মন্তব্য : ব্লক কমেন্ট (
/* ... */) ব্যবহার করুন। লাইন কমেন্ট (--) ব্যবহার করা নিষিদ্ধ, কারণ কোয়েরি সংযুক্ত করার সময় এগুলি পরবর্তী ক্লজগুলিকে (যেমন সিকিউরিটি ফিল্টার) কেটে ফেলতে পারে। - প্যারামিটার :
paramsঅ্যারের ক্রম অনুসারে পজিশনাল প্যারামিটার ($1,$2) ব্যবহার করুন। নেমড প্যারামিটার ($id,:name) সমর্থিত নয়। - স্ট্রিং : বর্ধিত স্ট্রিং লিটারেল (
E'...') এবং ডলার-কোটেড স্ট্রিং ($$...$$) সমর্থিত। PostgreSQL ইউনিকোড এস্কেপ (U&'...') সমর্থিত নয়।
মন্তব্যে প্যারামিটার
পার্সার ব্লক কমেন্টের ভেতরের সবকিছু উপেক্ষা করে। যদি আপনি কোনো প্যারামিটারযুক্ত লাইন কমেন্ট করে দেন (উদাহরণস্বরূপ, /* WHERE id = $1 */ ), তাহলে আপনাকে অবশ্যই params তালিকা থেকেও সেই প্যারামিটারটি সরিয়ে ফেলতে হবে, নতুবা অপারেশনটি unused parameter: $1 ত্রুটিসহ ব্যর্থ হবে।
উদাহরণ
উদাহরণ ১: ফিল্ড অ্যালিয়াসিং সহ সাধারণ 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!) {
_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]
)
}
উদাহরণ ৭: পোস্টগ্রেস এক্সটেনশন ব্যবহার
নেটিভ SQL আপনাকে আপনার GraphQL স্কিমাতে জটিল জ্যামিতি টাইপ ম্যাপ করা বা আপনার অন্তর্নিহিত টেবিল পরিবর্তন করার প্রয়োজন ছাড়াই PostGIS-এর মতো Postgres এক্সটেনশন ব্যবহার করার সুযোগ দেয়।
এই উদাহরণে, ধরুন আপনার রেস্তোরাঁ অ্যাপে একটি টেবিল আছে যা একটি মেটাডেটা 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]
)
}
ক্লায়েন্ট SDK ব্যবহার করে কোয়েরিটি চালানোর পর, ফলাফলটি data.nearby তে পাওয়া যাবে।
নিরাপত্তার সর্বোত্তম অনুশীলন: ডায়নামিক SQL এবং স্টোরড প্রসিডিউর
Data Connect গ্রাফকিউএল-টু-ডাটাবেস সীমানায় সমস্ত ইনপুটকে নিরাপদে প্যারামিটারাইজ করে, যা আপনার স্ট্যান্ডার্ড SQL কোয়েরিগুলোকে ফার্স্ট-অর্ডার SQL ইনজেকশন থেকে সম্পূর্ণরূপে সুরক্ষিত রাখে। তবে, আপনি যদি কাস্টম পোস্টগ্রেস স্টোরড প্রসিডিউর বা ডাইনামিক 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;
$$;