PostgreSQL æ°æ®åº
CloudBase æä¾äº PostgreSQL æ°æ®åº æå¡ï¼åºäºå¼æº PostgREST æå»ºï¼æ¯æå®æ´ç SQL åè½ï¼å¹¶éè¿ è¡¨çº§ GRANT + è¡çº§ RLS Policy çå屿鿍¡åï¼å®ç°å®¢æ·ç«¯å¯ç´è¿æ°æ®åºçå®å ¨è®¿é®è½åã
é 读å建议å äºè§£ PG æ¨¡å¼æ¦è¿°ï¼æç¡® PostgreSQL å¨äºå¼åç¯å¢ä¸çæ´ä½å®ä½ã
CloudBase è¿æä¾ ææ¡£åæ°æ®åº å MySQL æ°æ®åºï¼å¯æ ¹æ®ä¸å¡éæ±éæ©åéçæ°æ®åºç±»åã
ä½¿ç¨ AI 管çå ³ç³»åæ°æ®åºå SQL æä½
PG 模å¼ï¼ä»¥ PostgreSQL 为ä¸å¿â
CloudBase PostgreSQL æ°æ®åºä¸åªæ¯"å¤äºä¸ä¸ªæ°æ®åºé项"ï¼å®ä»£è¡¨äºä¸ç§ä»¥ PostgreSQL 为ä¸å¿çç¯å¢å½¢æââPG 模å¼ãå¨è¿ç§å½¢æä¸ï¼PostgreSQL ä¸ä» æ¿æ ä¸å¡æ°æ®åå¨ï¼è¿ä½ä¸ºç»ä¸åºç¡è®¾æ½ï¼è®©è´¦å·ãæéãäºåå¨å æ°æ®é½è½å°åä¸ä¸ªæ°æ®åºå ï¼
- ä¸å¡æ°æ®ï¼ç´æ¥åè¿ PostgreSQL 表ï¼å®¢æ·ç«¯éè¿ PostgREST èªå¨æ´é²ç RESTful API ç´è¿è¯»å
- æé模åï¼ä»¥ SQL 表达ââ表级
GRANT+ è¡çº§RLS Policyå屿 ¡éªï¼è¯·æ±èªå¸¦ JWT ç±æ°æ®åºå¤å®æé - è´¦å·ç³»ç»ï¼ç¨æ·æ°æ®åå¨å¨
authschemaï¼å¯ä»¥ç´æ¥ç¨ SQL æ¥è¯¢ãå ³èå°ä¸å¡è¡¨ - äºåå¨ï¼æä»¶å
æ°æ®åå¨å¨
storageschemaï¼æéåæ ·ç¨ RLS 表达ï¼ä¸ä¸å¡æ°æ®å ±äº«ä¸å¥æéè¯è¨
è¿æå³çå¼åè åªéææ¡ SQL ä¸å¥è¡¨è¾¾æ¹å¼ï¼å°±è½å®ææ°æ®å»ºæ¨¡ãæé设计ãè·¨ååä½ãæ¬ç« åç»ææ¡£ï¼å¿«é䏿ãæé管çãRPC çï¼é»è®¤å°±å¨è¿ä¸ªä¸ä¸æä¸è®¨è®ºï¼ä¸åéå¤ "PG 模å¼" è¿ä¸åç¼ã
è½åéè§â
| è½å | 说æ |
|---|---|
| 宿´ SQL | 表ãè§å¾ãå¤é®ãç´¢å¼ãäºå¡ã触åå¨ãåå¨è¿ç¨ãCTEãçªå£å½æ°ãååºç |
| PostgREST RESTful API | 表 / è§å¾ / RPC èªå¨æ´é²ä¸º REST æ¥å£ï¼æ¯æçéãæåºãå页ãå ³èæ¥è¯¢ãåµå¥åå ¥ |
| å屿é | GRANTï¼è¡¨çº§ï¼ + RLS Policyï¼è¡çº§ï¼ï¼åééå® |
| ä¸ç§è®¿é®è§è² | anon / authenticated / service_role |
| æ©å±çæ | pgvector + vectorscaleãtencentdb_aiãzhparser / pg_jiebaï¼ä¸æåè¯ï¼ãTimescaleDBãApache AGEãPL/V8 ç |
| 身份认è¯èå¨ | auth schema + JWT claims å¯ç´æ¥å¨ SQL ä¸è¯»åç¨æ·èº«ä»½ |
| äºåå¨èå¨ | storage schema åå¨å¯¹è±¡å
æ°æ®ï¼å¯ä¸ä¸å¡è¡¨ JOIN |
å¦ä½å¯ç¨â
彿¨å建 CloudBase PostgreSQL çæ¬ç¯å¢æ¶ï¼PostgreSQL æ°æ®åºä¼èªå¨å¯ç¨ï¼æ éæå¨å建æåå§åæ°æ®åºå®ä¾ã
è®¿é®æ°æ®åºçä¸ç§æ¹å¼â
1. 客æ·ç«¯ SDKï¼ç´è¿ + RLSï¼â
æå¸¸ç¨çæ¹å¼ãå端éè¿äºå¼å SDK ç´æ¥è¯»åæ°æ®åºï¼ç± RLS 宿鴿ï¼
import cloudbase from '@cloudbase/js-sdk';
// éæ©ä¸ç§é´ææ¹å¼ï¼
// (1) ä½¿ç¨ Publishable Keyï¼å端å¯è§ï¼ï¼accessKey ä¼ å
¥å³å¯ï¼æææªæ¾å¼ç»å½ç请æ±é½ä»¥ anon 身份访é®
const app = cloudbase.init({ env: '<env-id>', accessKey: '<Publishable Key>' });
// (2) æä¸ä¼ accessKeyï¼å端è°ç¨ auth.signInAnonymously() / signInWithPassword() çæ¾å¼ç»å½
// const app = cloudbase.init({ env: '<env-id>' });
// await app.auth.signInAnonymously();
const db = app.rdb();
// æ¥è¯¢ï¼RLS èªå¨æç»å½ç¨æ·è¿æ»¤ï¼
const { data } = await db
.from('todos')
.select('id, title, is_completed')
.eq('is_completed', false);
// æ°å¢ï¼owner_id ç±æ°æ®åºé»è®¤å¼èªå¨å¡«å
为 JWT ç subï¼
await db.from('todos').insert({ title: 'åä¸ç¯ææ¡£' });
â ï¸ SDK 请æ±å¿ é¡»æºå¸¦ JWTï¼Publishable Keyãaccess_token æ API Key ä¸éä¸ï¼ãæªæä¾æ¶ç½å ³ä¼æç»è¯·æ±ã
äºå¼åæä¾äºå¤ç§ SDK ä¾å¼åè æä½ PostgreSQL æ°æ®åºï¼
| SDK ç±»å | éç¨å¹³å° |
|---|---|
| å°ç¨åº ClientSDK | å°ç¨åº |
| JS SDK | Web æµè§å¨ |
| Node SDK | Node.js ç¯å¢ |
| HTTP API | éç¨ |
å°ç¨åº ClientSDK è·å db å®ä¾åï¼æä½ PostgreSQL æ°æ®åºè¯æ³ä¸ Web JS SDK ä¸è´ï¼å ·ä½è¯æ³è¯·åè JS SDKã
2. REST APIï¼PostgREST è§èï¼â
éç¨äºæ SDK åºæ¯ï¼å ¶ä»å端ãä½ä»£ç å¹³å°ãç¬¬ä¸æ¹ç³»ç»ï¼ã
åºç¡ç«¯ç¹ï¼
REST: https://<envId>.api.tcloudbasegateway.com/v1/rdb/rest/<table>
Auth: https://<envId>.api.tcloudbasegateway.com/auth/v1
认è¯å¤´ï¼ Authorization: Bearer <Publishable Key | access_token | API Key>
è°ç¨ç¤ºä¾ï¼
# ååºååï¼å¿åï¼ä½¿ç¨ Publishable Keyï¼
curl "https://<envId>.api.tcloudbasegateway.com/v1/rdb/rest/products?select=id,name,price&order=price.desc" \
-H "Authorization: Bearer <Publishable Key>"
# ç»å½åä¸åï¼ä½¿ç¨ access_tokenï¼
curl -X POST "https://<envId>.api.tcloudbasegateway.com/v1/rdb/rest/orders" \
-H "Authorization: Bearer <access_token>" \
-H "Prefer: return=representation" \
-H "Content-Type: application/json" \
-d '{"product_id":1,"quantity":2,"total_price":199.00}'
è¯¦è§ HTTP API - PostgREST RESTful APIã
3. äº API ExecutePGSqlï¼ç®¡æ§é¢ï¼â
äº API ExecutePGSql æ¯ç®¡çåæ§è¡ä»»æ SQL çè½åï¼å¸¸ç¨äº CI/CD èªå¨åé¨ç½²ãæ°æ®åº schema è¿ç§»ãèªå¨å建表çåºæ¯ã
åºæ¬ä¿¡æ¯ï¼
| åæ®µ | å¼ |
|---|---|
| æ¥å£åç§° | ExecutePGSql |
| ç«¯ç¹ | tcb.tencentcloudapi.com |
| Version | 2018-06-08 |
| Region | å¿
å¡«ï¼ä¸ç¯å¢æå¨å°åä¸è´ï¼å¦ ap-shanghaiï¼ |
| ç¾åæ¹å¼ | TC3-HMAC-SHA256 |
请æ±ä½ï¼
{
"EnvId": "<envId>",
"Sql": "<è¦æ§è¡ç SQL>",
"Role": "<å¯éï¼ä»¥æå®çæ°æ®åºè§è²æ§è¡ï¼å¸¸ç¨å¼ï¼anon / authenticated / service_role>"
}
Roleåæ°å¯çç¥ï¼
- çç¥æ¶ï¼ä»¥ç³»ç»è¶ 级管çåè§è²
cloudbase_adminæ§è¡ï¼å ·å¤SUPERUSER+BYPASSRLSæéï¼å¯æ§è¡ä»»æ DDLï¼å æ¬CREATE TABLEçç»æåæ´ï¼åç»è¿ææ RLS çç¥- æå®
Roleåï¼å¸¸ç¨å¼ï¼anon/authenticated/service_roleï¼ï¼ä»¥æå®è§è²æ§è¡ SQLï¼ä¾¿äºå¨ç®¡æ§é¢æ¨¡æç¹å®è§è²è°è¯ RLS çç¥ï¼éªè¯çç¥æ¯å¦ç¬¦åé¢æãæ³¨ææ¤æ¶å GRANT + RLS 约æ
å
³äº DDL è¯å¥ï¼é¨å DDLï¼CREATE / ALTER / DROP / GRANT / REVOKE / TRUNCATE / COMMENT çï¼å¨éè¿ ExecutePGSql ç´æ¥æ§è¡æ¶å¯è½è¿å InternalErrorãéå°å¤±è´¥æ¶ï¼å¯å°è¯¥ DDL å
è£
为å¿å代ç åéè¯ãå¿å代ç åéè¦ BEGIN ... END ä¸å¨æ EXECUTEï¼å æ¤è¿éæ¾å¼ä½¿ç¨ LANGUAGE plpgsqlï¼æ®é彿°ç¤ºä¾ä»é»è®¤ä½¿ç¨ LANGUAGE SQLï¼
-- ç´æ¥æ§è¡å¯è½æ¥éç DDL
CREATE TABLE public.products (id serial PRIMARY KEY, name text);
-- 失败æ¶å
è£
为 DO $$ 代ç åéè¯
DO LANGUAGE plpgsql $$
BEGIN
EXECUTE 'CREATE TABLE public.products (id serial PRIMARY KEY, name text)';
END
$$;
SQL ä¸çåå¼å·
'å¨EXECUTE '...'å é转ä¹ä¸º''ã
Python 示ä¾ï¼ä½¿ç¨è ¾è®¯äº SDKï¼ï¼
pip install tencentcloud-sdk-python
import json, re
from tencentcloud.common import credential
from tencentcloud.common.profile.client_profile import ClientProfile
from tencentcloud.common.profile.http_profile import HttpProfile
from tencentcloud.tcb.v20180608 import tcb_client, models
SECRET_ID = "<your SecretId>"
SECRET_KEY = "<your SecretKey>"
ENV_ID = "<your envId>"
REGION = "ap-shanghai"
_DDL_KEYWORDS = re.compile(
r"^\s*(CREATE|DROP|ALTER|GRANT|REVOKE|TRUNCATE|COMMENT|"
r"SET|RESET|LOCK|REINDEX|CLUSTER|VACUUM|ANALYZE)\b",
re.IGNORECASE,
)
def _wrap_ddl(sql: str) -> str:
return f"DO LANGUAGE plpgsql $$ BEGIN EXECUTE '{sql.replace(chr(39), chr(39)*2)}'; END $$"
def execute_sql(sql: str, role: str = None, retry_with_wrap: bool = True) -> dict:
cred = credential.Credential(SECRET_ID, SECRET_KEY)
http_profile = HttpProfile()
http_profile.endpoint = "tcb.tencentcloudapi.com"
client_profile = ClientProfile()
client_profile.httpProfile = http_profile
client = tcb_client.TcbClient(cred, REGION, client_profile)
body = {"EnvId": ENV_ID, "Sql": sql}
if role:
body["Role"] = role
try:
req = models.ExecutePGSqlRequest()
req.from_json_string(json.dumps(body))
return json.loads(client.ExecutePGSql(req).to_json_string())
except Exception as e:
# é¨å DDL ç´æ¥æ§è¡ä¼å¤±è´¥ï¼èªå¨ç¨ DO $$ å
è£
é è¯
if retry_with_wrap and _DDL_KEYWORDS.match(sql.strip()):
return execute_sql(_wrap_ddl(sql), role=role, retry_with_wrap=False)
raise
# DML ç´æ¥æ§è¡
print(execute_sql("SELECT 1"))
# DDL èªå¨éè¯ï¼å¤±è´¥æ¶ç¨ DO $$ å
è£
ï¼
execute_sql("""
CREATE TABLE public.products (
id serial PRIMARY KEY,
name text NOT NULL,
price numeric(10,2) NOT NULL
)
""")
execute_sql("ALTER TABLE public.products ENABLE ROW LEVEL SECURITY")
execute_sql("GRANT SELECT ON public.products TO anon")
- æ¯æ¬¡ API è°ç¨åªè½æ§è¡ä¸æ¡ SQLï¼ä¸è½ç¨åå·æ¼æ¥å¤æ¡ï¼æ¹éæ§è¡è¯·æè¡/ åå·æååéæ¡è°ç¨
- DDL 失败æ¶ä½¿ç¨
DO LANGUAGE plpgsql $$ BEGIN EXECUTE '...'; END $$å è£ ï¼æ³¨æåå¼å·è½¬ä¹ã该å¿å代ç åä¾èµBEGINå卿EXECUTEï¼å æ¤éè¦LANGUAGE plpgsql - API å¶å°å¯è½å åç«¯çææ
éè¿å
InternalErrorï¼å»ºè®®å å ¥ææ°éé¿éè¯ - æ¬æ¥å£å±äºç®¡çåæä½ï¼æéé«ï¼ä» å¨å端使ç¨ï¼ä¸è¦æ´é² SecretKey
å屿鿍¡åâ
æ°æ®åºæéç±ä¸¤å±ç»æï¼è¯·æ±ä¸¤å±é½éè¿æä¼æåï¼
è¯·æ± â JWT è§£æ â 第ä¸å±ï¼è¡¨çº§ GRANTï¼è½æ§è¡åªäºæä½ï¼
â 第äºå±ï¼RLS Policyï¼è½è®¿é®åªäºè¡ï¼â è¿åç»æ
第ä¸å±ï¼GRANTâ
-- 示ä¾ï¼å¿åå¯è¯»ï¼ç»å½ç¨æ·å¯è¯»åï¼ç®¡çåå
¨é¨æé
GRANT SELECT ON public.orders TO anon;
GRANT SELECT, INSERT, UPDATE, DELETE ON public.orders TO authenticated;
GRANT ALL ON public.orders TO service_role;
-- ä½¿ç¨ serial 䏻鮿¶ï¼è¿éæäºåºåæé
GRANT USAGE, SELECT ON SEQUENCE public.orders_id_seq TO authenticated;
第äºå±ï¼RLS Policyâ
ALTER TABLE public.orders ENABLE ROW LEVEL SECURITY;
-- å¿åç¨æ·å¯è§ï¼ä»
å
¬å¼è®¢åï¼ç¤ºä¾ï¼
CREATE POLICY orders_anon_select ON public.orders
FOR SELECT TO anon
USING (is_public = true);
-- ç»å½ç¨æ·å¯è§ï¼ä»
èªå·±ç
CREATE POLICY orders_auth_select ON public.orders
FOR SELECT TO authenticated
USING (buyer_id = (current_setting('request.jwt.claims', true)::json->>'sub'));
service_roleç±äºå ·å¤ BYPASSRLSï¼èªå¨ç»è¿ RLS Policyãåªè¦å¨ç¬¬ä¸å± GRANT äºå¯¹åºæéå³å¯ï¼æ é为å®å Policyã
åç§å¸¸è§ RLS 模å¼â
以ä¸åç§æ¨¡å¼è¦çç»å¤§å¤æ°ä¸å¡åºæ¯ï¼å¯ä»¥ç´æ¥å¤ç¨ï¼å½å沿è¢ä¼ ç»æ¨¡å¼äºåå¨çååæé模å¼ï¼ä¾¿äºå¿æºè¿ç§»ï¼ï¼
| æ¨¡å¼ | å ¸ååºæ¯ | æéææ |
|---|---|---|
READONLY | å客æç« ã产åç®å½ | ææäººå¯è¯»ï¼ä» å建è å¯å |
PRIVATE | ç§äººç¬è®°ã个人设置 | ä» å建è å¯è¯»åï¼ç¨æ·é´å®å ¨éç¦»ï¼ |
ADMINWRITE | å ¬åãç³»ç»é ç½® | ææäººå¯è¯»ï¼ä» 管çåå¯å |
ADMINONLY | 审计æ¥å¿ãåå°é ç½® | ä» ç®¡çåå¯è¯»å |
å¤æåºæ¯ï¼å¤ç§æ· SaaS åä½ã社交å¯è§æ§ãå¯è§æ§è·éå¸åçï¼å¯éè¿åæ¥è¯¢ / EXISTS / OR æ¡ä»¶çµæ´»è¡¨è¾¾ã
è¯¦è§ RLS æé模å¼åº ä¸ åºç¡æé管çã
æ¥è¯¢è¡¨è¾¾å¼ï¼PostgREST 约å®ï¼â
REST API æ¯æä¸°å¯çæ¥è¯¢åæ°ï¼
# è¿æ»¤
?category=eq.çµå产å # 精确å¹é
?price=gte.1000&price=lte.5000 # èå´
?name=like.*iPhone* # 模ç³
?status=in.(pending,paid) # IN
# æåº
?order=price.desc
?order=category.asc,price.desc
# å页
?limit=10&offset=0
# åéæ©
?select=id,name,price
# å
³èæ¥è¯¢ï¼åºäºå¤é®èªå¨æ¨æï¼
?select=*,products(name,price) # 订å + å
³èåå
å¸¸è§æ¥éå¤çâ
xxx.rdb is not a functionâ
æ¥éåå ï¼å½å使ç¨ç CloudBase SDK çæ¬è¿æ§ï¼ä¸æ¯æ PostgreSQL æ°æ®åºæä½ã
è§£å³æ¹æ³ï¼æ´æ°å½å SDK å°ææ°çæ¬ã
Generating default gateway base url failed: env not foundâ
æ¥éåå ï¼å¾®ä¿¡åºç¡åºçæ¬è¿ä½ï¼ä¸æ¯æäºå¼å PostgreSQL æ°æ®åºåè½ã
è§£å³æ¹æ³ï¼æ´æ°å¾®ä¿¡åºç¡åº å° 3.8.9 çæ¬ä»¥ä¸ã
å¨å¾®ä¿¡å¼åè å·¥å ·ä¸è®¾ç½®æä½åºç¡åºçæ¬ï¼
- æå¼ã项ç®è¯¦æ ã
- å¨ãæ¬å°è®¾ç½®ãä¸ï¼å°ãè°è¯åºç¡åºã设置为 3.8.9 ææ´é«çæ¬
- å¨ãåºæ¬ä¿¡æ¯ãä¸ï¼å°ãæä½åºç¡åºçæ¬ã设置为 3.8.9 ææ´é«çæ¬
æ´æ°åºç¡åºçæ¬åï¼è¯·ç¡®ä¿å°ç¨åºå¨ä½çæ¬å¾®ä¿¡ä¸çå ¼å®¹æ§å¤çï¼æå¨å°ç¨åºç®¡çåå°è®¾ç½®æä½çæ¬è¦æ±ã
æä½³å®è·µä¸å¸¸è§é·é±â
æ¨èåæ³ï¼
- ä¸å¡è¡¨å½å±å段使ç¨
DEFAULTç»å® JWTsubï¼ç¦æ¢åç«¯ä¼ å ¥ - UPDATE çç¥åæ¶è®¾ç½®
USINGåWITH CHECK - GRANT + RLS åééå®ï¼ä¸å¸ææè§è²åå°±å¨ GRANT 屿¦æª
- 使ç¨
serial/bigserial䏻鮿¶å«å¿è®°æäºSEQUENCEçæé - æ¸ çæµè¯æ°æ®æ¶æ³¨æå¤é®é¡ºåºï¼å å å表åå ç¶è¡¨
常è§é·é±ï¼
- å¯ç¨ RLS åå¿è®°å»º Policy â ææé
service_role请æ±é½è¢«æ - å»ºäº Policy 使²¡ GRANT â 表级æéä¸éè¿
- å端æ´é² API Key â
service_roleBYPASSRLSï¼æææ°æ®æ´é² - UPDATE åªåäº
USINGæªåWITH CHECKâ ç¨æ·å¯ä»¥éè¿ UPDATE 篡æ¹å½å±å段"çªå"æ°æ® - 对å¿åç¨æ·æäºè¿å¤æéï¼é¤éæå¼ºè¯æ±ï¼
anonåªç»SELECTï¼
è¯¦ç»ææ¡£å ¥å£â
- åºç¡æé管ç
- RLS æé模å¼åº
- æ°æ®åºå½æ°ï¼RPCï¼
- å¤é®ç®¡ç
- ç´¢å¼ç®¡ç
- HTTP API - PostgREST RESTful API
- Web SDK - PostgreSQL æ°æ®åº
- Node.js SDK - PostgreSQL æ°æ®åº
æ¡æ¶å¿«éå ¥é¨â
ä¸ä¸æ¥â
- å¿«éä½éª â 5 åéè·éå®å ¨çå¤ç¨æ·æ°æ®è®¿é®
- 宿æç¨ï¼çµåå°ç¨åº â 综ååºç¨å»ºæ¨¡ + æé设计 + REST API
- PGï¼èº«ä»½è®¤è¯ â JWTãä¸è§è²ä¸ Key 管ç
- PG 模å¼äºåå¨ â åºäº RLS çæä»¶æé
- åºç¡æé管ç
- ç´¢å¼ç®¡ç