国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

精讀《sqorn 源碼》

Youngs / 3169人閱讀

摘要:引言前端精讀手寫編譯器系列介紹了如何利用生成語法樹,而還有一些庫的作用是根據(jù)語法樹生成語句。對,有利就有弊,這些庫不遵循語法樹,但利用簡化的對象模型快速生成,使得代碼抽象程度得到了提高。

1 引言

前端精讀《手寫 SQL 編譯器系列》 介紹了如何利用 SQL 生成語法樹,而還有一些庫的作用是根據(jù)語法樹生成 SQL 語句。

除此之外,還有一種庫,是根據(jù)編程語言生成 SQL。sqorn 就是一個(gè)這樣的庫。

可能有人會(huì)問,利用編程語言生成 SQL 有什么意義?既沒有語法樹規(guī)范,也不如直接寫 SQL 通用。對,有利就有弊,這些庫不遵循語法樹,但利用簡化的對象模型快速生成 SQL,使得代碼抽象程度得到了提高。而代碼抽象程度得到提高,第一個(gè)好處就是易讀,第二個(gè)好處就是易操作。

數(shù)據(jù)庫特別容易抽象為面向?qū)ο竽P停鴮?shù)據(jù)庫的操作語句 - SQL 是一種結(jié)構(gòu)化查詢語句,只能描述一段一段的查詢,而面向?qū)ο竽P蛥s適合描述一個(gè)整體,將數(shù)據(jù)庫多張表串聯(lián)起來。

舉個(gè)例子,利用 typeorm,我們可以用 ab 兩個(gè) Class 描述兩張表,同時(shí)利用 ManyToMany 裝飾器分別修飾 ab 的兩個(gè)字段,將其建立起 多對多的關(guān)聯(lián),而這個(gè)映射到 SQL 結(jié)構(gòu)是三張表,還有一張是中間表 ab,以及查詢時(shí)涉及到的 left join 操作,而在 typeorm 中,一條 find 語句就能連帶查詢處多對多關(guān)聯(lián)關(guān)系。

這就是這種利用編程語言生成 SQL 庫的價(jià)值,所以本周我們分析一下 sqorn 這個(gè)庫的源碼,看看利用對象模型生成 SQL 需要哪些步驟。

2 概述

我們先看一下 sqorn 的語法。

const sq = require("sqorn-pg")();

const Person = sq`person`,
  Book = sq`book`;

// SELECT
const children = await Person`age < ${13}`;
// "select * from person where age < 13"

// DELETE
const [deleted] = await Book.delete({ id: 7 })`title`;
// "delete from book where id = 7 returning title"

// INSERT
await Person.insert({ firstName: "Rob" });
// "insert into person (first_name) values ("Rob")"

// UPDATE
await Person({ id: 23 }).set({ name: "Rob" });
// "update person set name = "Rob" where id = 23"

首先第一行的 sqorn-pg 告訴我們 sqorn 按照 SQL 類型拆成不同分類的小包,這是因?yàn)椴煌瑪?shù)據(jù)庫支持的方言不同,sqorn 希望在語法上抹平數(shù)據(jù)庫間差異。

其次 sqorn 也是利用面向?qū)ο笏季S的,上面的例子通過 sq`person` 生成了 Person 實(shí)例,實(shí)際上也對應(yīng)了 person 表,然后 Person`age < ${13}` 表示查詢:select * from person where age < 13

上面是利用 ES6 模板字符串的功能實(shí)現(xiàn)的簡化 where 查詢功能,sqorn 主要還是利用一些函數(shù)完成 SQL 語句生成,比如 where delete insert 等等,比較典型的是下面的 Example:

sq.from`book`.return`distinct author`
  .where({ genre: "Fantasy" })
  .where({ language: "French" });
// select distinct author from book
// where language = "French" and genre = "Fantsy"

所以我們閱讀 sqorn 源碼,探討如何利用實(shí)現(xiàn)上面的功能。

3 精讀

我們從四個(gè)方面入手,講明白 sqorn 的源碼是如何組織的,以及如何滿足上面功能的。

方言

為了實(shí)現(xiàn)各種 SQL 方言,需要在實(shí)現(xiàn)功能之前,將代碼拆分為內(nèi)核代碼與拓展代碼。

內(nèi)核代碼就是 sqorn-sql 而拓展代碼就是 sqorn-pg,拓展代碼自身只要實(shí)現(xiàn) pg 數(shù)據(jù)庫自身的特殊邏輯, 加上 sqorn-sql 提供的核心能力,就能形成完整的 pg SQL 生成功能。

實(shí)現(xiàn)數(shù)據(jù)庫連接

sqorn 不但生成 query 語句,也會(huì)參與數(shù)據(jù)庫連接與運(yùn)行,因此方言庫的一個(gè)重要功能就是做數(shù)據(jù)庫連接。sqorn 利用 pg 這個(gè)庫實(shí)現(xiàn)了連接池、斷開、查詢、事務(wù)的功能。

覆寫接口函數(shù)

內(nèi)核代碼想要具有拓展能力,暴露出一些接口讓 sqorn-xx 覆寫是很基本的。

context

內(nèi)核代碼中,最重要的就是 context 屬性,因?yàn)槿祟惲?xí)慣一步一步寫代碼,而最終生成的 query 語句是連貫的,所以這個(gè)上下文對象通過 updateContext 存儲(chǔ)了每一條信息:

{
  name: "limit",
  updateContext: (ctx, args) => {
    ctx.lim = args
  }
}

{
  name: "where",
  updateContext: (ctx, args) => {
    ctx.whr.push(args)
  }
}

比如 Person.where({ name: "bob" }) 就會(huì)調(diào)用 ctx.whr.push({ name: "bob" }),因?yàn)?where 條件是個(gè)數(shù)組,因此這里用 push,而 limit 一般僅有一個(gè),所以 context 對 lim 對象的存儲(chǔ)僅有一條。

其他操作諸如 where delete insert with from 都會(huì)類似轉(zhuǎn)化為 updateContext,最終更新到 context 中。

創(chuàng)建 builder

不用太關(guān)心下面的 sqorn-xx 包名細(xì)節(jié),這一節(jié)主要目的是說明如何實(shí)現(xiàn) Demo 中的鏈?zhǔn)秸{(diào)用,至于哪個(gè)模塊放在哪并不重要(如果要自己造輪子就要仔細(xì)學(xué)習(xí)一下作者的命名方式)。

sqorn-core 代碼中創(chuàng)建了 builder 對象,將 sqorn-sql 中創(chuàng)建的 methods merge 到其中,因此我們可以使用 sq.where 這種語法。而為什么可以 sq.where().limit() 這樣連續(xù)調(diào)用呢?可以看下面的代碼:

for (const method of methods) {
  // add function call methods
  builder[name] = function(...args) {
    return this.create({ name, args, prev: this.method });
  };
}

這里將 where delete insert with frommethods merge 到 builder 對象中,且當(dāng)其執(zhí)行完后,通過 this.create() 返回一個(gè)新 builder,從而完成了鏈?zhǔn)秸{(diào)用功能。

生成 query

上面三點(diǎn)講清楚了如何支持方言、用戶代碼內(nèi)容都收集到 context 中了,而且我們還創(chuàng)建了可以鏈?zhǔn)秸{(diào)用的 builder 對象方便用戶調(diào)用,那么只剩最后一步了,就是生成 query。

為了利用 context 生成 query,我們需要對每個(gè) key 編寫對應(yīng)的函數(shù)做處理,拿 limit 舉例:

export default ctx => {
  if (!ctx.lim) return;
  const txt = build(ctx, ctx.lim);
  return txt && `limit ${txt}`;
};

context.lim 拿取 limit 配置,組合成 limit xxx 的字符串并返回就可以了。

build 函數(shù)是個(gè)工具函數(shù),如果 ctx.lim 是個(gè)數(shù)組,就會(huì)用逗號(hào)拼接。

大部分操作比如 delete from having 都做這么簡單的處理即可,但像 where 會(huì)相對復(fù)雜,因?yàn)閮?nèi)部包含了 condition 子語法,注意用 and 拼接即可。

最后是順序,也需要在代碼中確定:

export default {
  sql: query(sql),
  select: query(wth, select, from, where, group, having, order, limit, offset),
  delete: query(wth, del, where, returning),
  insert: query(wth, insert, value, returning),
  update: query(wth, update, set, where, returning)
};

這個(gè)意思是,一個(gè) select 語句會(huì)通過 wth, select, from, where, group, having, order, limit, offset 的順序調(diào)用處理函數(shù),返回的值就是最終的 query。

4 總結(jié)

通過源碼分析,可以看到制作一個(gè)這樣的庫有三個(gè)步驟:

創(chuàng)建 context 存儲(chǔ)結(jié)構(gòu)化 query 信息。

創(chuàng)建 builder 供用戶鏈?zhǔn)綍鴮懘a同時(shí)填充 context。

通過若干個(gè) SQL 子處理函數(shù)加上幾個(gè)主 statement 函數(shù)將其串聯(lián)起來生成最終 query。

最后在設(shè)計(jì)時(shí)考慮到 SQL 方言的話,可以將模塊拆成 核心、SQL、若干個(gè)方言庫,方言庫基于核心庫做拓展即可。

5 更多討論
討論地址是:精讀《sqorn 源碼》 · Issue #103 · dt-fe/weekly

如果你想?yún)⑴c討論,請點(diǎn)擊這里,每周都有新的主題,周末或周一發(fā)布。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/97831.html

相關(guān)文章

  • 精讀源碼學(xué)習(xí)》

    摘要:精讀原文介紹了學(xué)習(xí)源碼的兩個(gè)技巧,并利用實(shí)例說明了源碼學(xué)習(xí)過程中可以學(xué)到許多周邊知識(shí),都讓我們受益匪淺。討論地址是精讀源碼學(xué)習(xí)如果你想?yún)⑴c討論,請點(diǎn)擊這里,每周都有新的主題,周末或周一發(fā)布。 1. 引言 javascript-knowledge-reading-source-code 這篇文章介紹了閱讀源碼的重要性,精讀系列也已有八期源碼系列文章,分別是: 精讀《Immer.js》源...

    aboutU 評(píng)論0 收藏0
  • 精讀《react-easy-state 源碼

    摘要:會(huì)自動(dòng)觸發(fā)函數(shù)內(nèi)回調(diào)函數(shù)的執(zhí)行。因此利用并將依賴置為使代碼在所有渲染周期內(nèi),只在初始化執(zhí)行一次。同時(shí)代碼里還對等公共方法進(jìn)行了包裝,讓這些回調(diào)函數(shù)中自帶效果。前端精讀幫你篩選靠譜的內(nèi)容。 1. 引言 react-easy-state 是個(gè)比較有趣的庫,利用 Proxy 創(chuàng)建了一個(gè)非常易用的全局?jǐn)?shù)據(jù)流管理方式。 import React from react; import { stor...

    curlyCheng 評(píng)論0 收藏0
  • 精讀《Inject Instance 源碼

    摘要:引言本周精讀的源碼是這個(gè)庫。這個(gè)庫的目的是為了實(shí)現(xiàn)的依賴注入。精讀那么開始源碼的解析,首先是整體思路的分析。討論地址是精讀源碼如果你想?yún)⑴c討論,請點(diǎn)擊這里,每周都有新的主題,周末或周一發(fā)布。前端精讀幫你篩選靠譜的內(nèi)容。 1. 引言 本周精讀的源碼是 inject-instance 這個(gè)庫。 這個(gè)庫的目的是為了實(shí)現(xiàn) Class 的依賴注入。 比如我們通過 inject 描述一個(gè)成員變量,...

    hsluoyz 評(píng)論0 收藏0
  • 精讀《syntax-parser 源碼

    摘要:引言是一個(gè)版語法解析器生成器,具有分詞語法樹解析的能力。實(shí)現(xiàn)函數(shù)用鏈表設(shè)計(jì)函數(shù)是最佳的選擇,我們要模擬調(diào)用棧了。但光標(biāo)所在的位置是期望輸入點(diǎn),這個(gè)輸入點(diǎn)也應(yīng)該參與語法樹的生成,而錯(cuò)誤提示不包含光標(biāo),所以我們要執(zhí)行兩次。 1. 引言 syntax-parser 是一個(gè) JS 版語法解析器生成器,具有分詞、語法樹解析的能力。 通過兩個(gè)例子介紹它的功能。 第一個(gè)例子是創(chuàng)建一個(gè)詞法解析器 my...

    yuanxin 評(píng)論0 收藏0
  • 精讀《Epitath 源碼 - renderProps 新用法》

    摘要:精讀源碼一共行,我們分析一下其精妙的方式。更多討論討論地址是精讀新用法如果你想?yún)⑴c討論,請點(diǎn)擊這里,每周都有新的主題,周末或周一發(fā)布。前端精讀幫你篩選靠譜的內(nèi)容。 1 引言 很高興這一期的話題是由 epitath 的作者 grsabreu 提供的。 前端發(fā)展了 20 多年,隨著發(fā)展中國家越來越多的互聯(lián)網(wǎng)從業(yè)者涌入,現(xiàn)在前端知識(shí)玲瑯滿足,概念、庫也越來越多。雖然內(nèi)容越來越多,但作為個(gè)體的...

    Magicer 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

Youngs

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<