Firebase Data Connect, Cloud SQL veritabanınızla etkileşim kurmanın birden fazla yolunu sunar:
- Yerel GraphQL:
schema.gqlve Data Connect içinde türleri tanımlayın. Bu türler, GraphQL işlemlerinizi SQL'e çevirir. Bu, güçlü türleme ve şema zorunlu yapılar sunan standart yaklaşımdır. Bu sayfa dışındaki Data Connect belgelerin çoğunda bu seçenek ele alınır. Mümkün olduğunda, tam tip güvenliği ve araç desteğinden yararlanmak için bu yöntemi kullanmanız gerekir. @viewyönergesi:schema.gqliçinde, özel birSELECTSQL ifadesiyle desteklenen bir GraphQL türü tanımlayın. Bu özellik, karmaşık SQL mantığına dayalı olarak salt okunur ve kesin olarak yazılmış görünümler oluşturmak için kullanışlıdır. Bu türler, normal türler gibi sorgulanabilir.@viewsayfasına göz atın.- Yerel SQL: SQL ifadelerini doğrudan . içindeki adlandırılmış işlemlere yerleştirin.
gqldosyaları özel kök alanları kullanılarak oluşturulur. Bu, özellikle standart GraphQL'de kolayca ifade edilemeyen, veritabanına özgü özelliklerden yararlanan veya PostgreSQL uzantılarını kullanan işlemler için maksimum esneklik ve doğrudan kontrol sağlar.
Bu kılavuzda Native SQL seçeneği ele alınmaktadır.
Doğal SQL'in yaygın kullanım alanları
Yerel GraphQL, tam tür güvenliği sağlarken @view yönergesi salt okunur SQL raporları için kesin türü belirlenmiş sonuçlar sunar. Yerel SQL ise aşağıdakiler için gereken esnekliği sağlar:
- PostgreSQL Uzantıları: GraphQL şemanızdaki karmaşık türleri eşlemenize gerek kalmadan, yüklenen tüm PostgreSQL uzantılarını (ör. coğrafi veriler için
PostGIS) doğrudan sorgulayın ve kullanın. - Karmaşık Sorgular: Birleştirmeler, alt sorgular, toplama işlemleri, pencere işlevleri ve saklı prosedürler içeren karmaşık SQL'leri yürütün.
- Veri Manipülasyonu (DML):
INSERT, UPDATE, DELETEişlemlerini doğrudan gerçekleştirin. (Ancak Veri Tanımlama Dili (DDL) komutları için yerel SQL kullanmayın. Arka ucunuzu ve oluşturulan SDK'larınızı senkronize tutmak için şema düzeyindeki değişiklikleri GraphQL kullanarak yapmaya devam etmeniz gerekir.) - Veritabanına Özgü Özellikler: PostgreSQL'e özgü işlevleri, operatörleri veya veri türlerini kullanın.
- Performans Optimizasyonu: Kritik yollar için SQL ifadelerini manuel olarak ayarlayın.
Yerel SQL kök alanları
SQL ile yazma işlemleri yapmak için query veya mutation türlerinin aşağıdaki kök alanlarından birini kullanın:
query alanı sayısı
| Alan | Açıklama |
|---|---|
_select |
Sıfır veya daha fazla satır döndüren bir SQL sorgusu yürütür. Bağımsız değişkenler:
Döndürür: JSON dizisi ( |
_selectFirst |
Sıfır veya bir satır döndürmesi beklenen bir SQL sorgusunu yürütür. Bağımsız değişkenler:
Döndürür: Bir JSON nesnesi ( |
mutation alanı sayısı
| Alan | Açıklama |
|---|---|
_execute |
Bir DML ifadesini yürütür ( Bağımsız değişkenler:
Döndürür: Sonuçta |
_executeReturning |
Bağımsız değişkenler:
Döndürür: JSON dizisi ( |
_executeReturningFirst |
Sıfır veya bir satır döndürmesi beklenen bir Bağımsız değişkenler:
Döndürür: Bir JSON nesnesi ( |
Notlar:
İşlemler, Data Connect hizmet hesabına verilen izinler kullanılarak yürütülür.
Tablo adını
@tableyönergesini kullanarak açıkça ayarlarsanız (@table(name: "ExampleTable")), SQL ifadelerinizde tablo adını da tırnak içine almanız gerekir (SELECT field FROM "ExampleTable" ...).Tırnak işaretleri olmadan Data Connect, tablo adını snake case'e (
example_table) dönüştürür.
Söz dizimi kuralları ve sınırlamaları
Yerel SQL, güvenliği sağlamak ve SQL eklenmesini önlemek için katı ayrıştırma kurallarını zorunlu kılar. Aşağıdaki kısıtlamalara dikkat edin:
- Yorumlar: Blok yorumları (
/* ... */) kullanın. Satır yorumları (--), sorgu birleştirme sırasında sonraki ifadeleri (ör. güvenlik filtreleri) kesebileceğinden yasaktır. - Parametreler:
paramsdizi sırasıyla eşleşen konumsal parametreler ($1,$2) kullanın. Adlandırılmış parametreler ($id,:name) desteklenmez. - Dizeler: Genişletilmiş dize değişmezleri (
E'...') ve dolar işaretli dizeler ($$...$$) desteklenir. PostgreSQL Unicode kaçışları (U&'...') desteklenmez.
Yorumlardaki parametreler
Ayrıştırıcı, blok yorumun içindeki her şeyi yok sayar. Parametre içeren bir satırı (örneğin, /* WHERE id = $1 */) yorum satırı haline getirirseniz bu parametreyi params listesinden de kaldırmanız gerekir. Aksi takdirde işlem unused parameter: $1 hatasıyla başarısız olur.
Örnekler
Örnek 1: Alan takma adıyla temel SELECT
Kök alanı (ör. movies: _select) takma adlandırarak istemci yanıtını daha temiz hale getirebilirsiniz (data._select yerine 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]
)
}
Sorgu, istemci SDK'sı kullanılarak çalıştırıldıktan sonra sonuç data.movies içinde yer alır.
Örnek 2: Temel 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]
)
}
Bir istemci SDK'sı kullanarak mutasyonu çalıştırdıktan sonra etkilenen satır sayısı data._execute içinde yer alır.
3. örnek: Temel toplama
queries.gql:
query GetTotalReviewCount @auth(level: PUBLIC) {
stats: _selectFirst(
sql: "SELECT COUNT(*) as total_reviews FROM \"Reviews\""
)
}
Sorgu, istemci SDK'sı kullanılarak çalıştırıldıktan sonra sonuç data.stats.total_reviews içinde yer alır.
Örnek 4: RANK ile gelişmiş toplama
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: []
)
}
Sorgu, istemci SDK'sı kullanılarak çalıştırıldıktan sonra sonuç data._select içinde yer alır.
Örnek 5: RETURNING ve Auth Context ile 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" }]
)
}
İstemci SDK'sı kullanılarak mutasyon çalıştırıldıktan sonra, güncellenen gönderi verileri data.updatedReview içinde yer alır.
6. örnek: Ekleme/güncelleme işlemleri içeren gelişmiş CTE (atomik get-or-create)
Bu desen, bağımlı kayıtların (ör. Kullanıcılar veya Filmler) alt kayıt (ör. Yorum) eklenmeden önce tek bir veritabanı işleminde mevcut olmasını sağlamak için kullanışlıdır.
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]
)
}
7. örnek: Postgres uzantılarını kullanma
Yerel SQL, karmaşık geometri türlerini GraphQL şemanıza eşlemeniz veya temel tablolarınızı değiştirmeniz gerekmeden PostGIS gibi Postgres uzantılarını kullanmanıza olanak tanır.
Bu örnekte, restoran uygulamanızın meta veri JSON sütununda (örneğin, {"latitude": 37.3688, "longitude": -122.0363}) konum verilerini depolayan bir tablosu olduğunu varsayalım. PostGIS uzantısını etkinleştirdiyseniz, bu değerleri anında ayıklamak ve PostGIS ST_MakePoint işlevine aktarmak için standart Postgres JSON operatörlerini (->>) kullanabilirsiniz.
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]
)
}
Sorgu, istemci SDK'sı kullanılarak çalıştırıldıktan sonra sonuç data.nearby içinde yer alır.
Güvenlikle ilgili en iyi uygulamalar: dinamik SQL ve saklı yordamlar
Data Connect GraphQL'den veritabanına sınırında tüm girişleri güvenli bir şekilde parametrelendirerek standart SQL sorgularınızı birinci derece SQL eklemeye karşı tamamen korur. Ancak, dinamik SQL'i yürüten özel Postgres saklı yordamlarını veya işlevlerini çağırmak için SQL kullanıyorsanız dahili PL/pgSQL kodunuzun bu parametreleri güvenli bir şekilde işlediğinden emin olmanız gerekir.
Saklı yordamınız, kullanıcı girişlerini doğrudan bir EXECUTE
dizeye birleştiriyorsa parametrelendirmeyi atlar ve ikinci dereceden bir SQL ekleme güvenlik açığı oluşturur:
-- 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;
$$;
Bunu önlemek için şu en iyi uygulamaları izleyin:
USINGifadesini kullanın: Saklı yordamlarınızda dinamik SQL yazarken veri parametrelerini güvenli bir şekilde bağlamak için her zamanUSINGifadesini kullanın.- Tanımlayıcılar için
format()kullanın: Güvenli veritabanı tanımlayıcı ekleme (ör. tablo adları) içinformat()ile%Iişaretini kullanın. - Tanımlayıcılara kesinlikle izin verin: İstemci uygulamalarının veritabanı tanımlayıcılarını rastgele seçmesine izin vermeyin. Prosedürünüz dinamik tanımlayıcılar gerektiriyorsa yürütmeden önce girişi PL/pgSQL mantığınızdaki sabit kodlanmış bir izin verilenler listesine göre doğrulayın.
-- 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;
$$;