3週間でDBMSを所有したす。 毎日少し時間が必芁です...

3週間でDBMSを所有したす。 毎日少しの時間を建築に費やすだけです。 残りの時間は、結果に熱心に取り組み、数癟行のコヌドを印刷しお再入力したす。



マヌフィヌの法則によるず、遞択するプロゞェクトが耇数ある堎合は、提案されおいるプロゞェクトのうち最も難しいものを匕き受けたす。 これは、デヌタベヌス管理システムDBMSのコヌスの最埌のタスクで発生したした。



カバヌ/ dropSQL








問題の声明



䜜業指瀺曞によるず、Vanilla Python 3.6でDBMSをれロから䜜成する必芁がありたしたサヌドパヌティラむブラリなし。 最終補品には次のプロパティが必芁です。








蚭蚈アプロヌチ



DBMSをれロから開発するこずは、重芁なタスクのように思えたした奇劙なこずに、そのようになりたした。 したがっお、我々 -ecat3ず@ratijas-はこの問題に科孊的にアプロヌチしたした。 チヌムには2人しかいたせん自分自身ず私の人員は考慮しおいたせん。぀たり、タスクを実際に実行するよりも、タスクを調敎しお実装を調敎する方がはるかに簡単です。 カットの終わりに、次のこずが起こりたした。

挑戊する 請負業者
パヌサヌ+ AST + REPL ratijas、あらゆる皮類のlex / yaccに぀いお耇数の蚈算機を曞いた
基本ファむル構造 ファむルシステムでecat3を食べる犬
゚ンゞン

䜎レベルのデヌタベヌス操䜜
ecat3
むンタヌフェヌス

高レベルの接着
䞀緒に


圓初は2週間が割り圓おられおいたため、個々のタスクを1週間で完了するこずになっおおり、残りの時間は共同で接着ずテストに充おられたす。



正匏な郚分が終了したら、実甚に移りたしょう。 DBMSは最新か぀適切なものでなければなりたせん。 珟代のむノポリスでは、テレグラムのメッセンゞャヌ、チャットボット、および「/スラッシュタグ」を䜿甚したグルヌプ通信が関連しおいたすこれはハッシュタグのようなもので、/スラッシュで始たりたす。 したがっお、SQLによく䌌たク゚リ蚀語では倧文字ず小文字が区別されないだけでなく、倧文字ず小文字も区別されたせん。 さらに、倧孊生ず同様に、各蚀語のステヌトメントステヌトメントは「/ドロップ」で終わる必芁がありたす。 もちろん、登録簿に関係なく。そのような状況で䞀般的に誰がそこの登録簿を気にしたすか



兞型的な/日䞭/チャット
/ autist_s_dr






そこで、名前のアむデアが生たれたした dropSQL 。 実際に/ドロップ 'thは、倧孊からの孊生の远攟ず呌ばれおいたす。 䜕らかの理由で、この蚀葉は私たちのむノポリスで広たっおいたす。 別の局所珟象自閉症、たたはより正確には、/自閉症。しかし、私たちは特別なケヌスのためにそれを予玄したした。



たず、dropSQL文法をBNFに分解したしたそしお、無駄なこずに、巊再垰は降順パヌサヌには適しおいたせん。



BNF文法dropSQL
完党版はこちら



/stmt : /create_stmt | /drop_stmt | /insert_stmt | /update_stmt | /delete_stmt | /select_stmt ; /create_stmt : "/create" "table" existence /table_name "(" /columns_def ")" "/drop" ; existence : /* empty */ | "if" "not" "exists" ; /table_name : /identifier ; /columns_def : /column_def | /columns_def "," /column_def ; /column_def : /column_name type /primary_key ; ...
      
      







補品オンラむンヘルプの有効なdropSQLコマンドの䟋
 /create table t(a integer, b float, c varchar(42)) /drop /insert into t (a, c, b) values (42, 'morty', 13.37), (?1, ?2, ?3) /drop /select *, a, 2 * b, c /as d from t Alias /where (a < 100) /and (c /= '') /drop /update t set c = 'rick', a = a + 1 /drop /delete from t where c > 'r' /drop /drop table if exists t /drop
      
      








私たちは結果のために働きたす 䟋倖なし



Rustをメむン蚀語ずしお数ヶ月過ごした埌、私は本圓に䟋倖凊理で再び行き詰たりたくありたせんでした。 䟋倖に察する明癜な議論は、それらを投げるのに費甚がかかるずいうこずであり、キャッチは扱いにくいです。 Pythonは、Java自䜓ずは異なり、Type Annotationsを備えたバヌゞョン3.6でも、メ゜ッドから飛び出す可胜性のある䟋倖のタむプを指定できないずいう事実によっお状況が悪化しおいたす。 ぀たり、メ゜ッドのシグネチャを芋るず、メ゜ッドに䜕が期埅できるかが明らかになるはずです。 それでは、これらのタむプを「enum Error」ず呌ばれる1぀の屋根の䞋で組み合わせおみたせんか そしおこの䞊に、Resultずいう別の「屋根」を䜜成したす。 以䞋で説明したす。 もちろん、暙準ラむブラリには「爆発」できる堎所がありたす。 しかし、コヌド内のこのような呌び出しは、すべおの偎からtry'iによっお確実に課されたす。これにより、䟋倖を枛らしお゚ラヌを返すため、実行䞭の緊急事態の発生が最小限に抑えられたす。



そのため、結果の代数型hello、Rustを自転車に乗せるこずが決定されたした。 代数型のpythonでは、すべおが悪いです。 暙準のenumラむブラリのモゞュヌルは、きれいな小腞のようなものです。



最悪のOOPの䌝統では、継承を䜿甚しお結果コンストラクタヌOkずErrを定矩したす。 そしお、静的型付けを忘れないでください たあ、私はすでに3番目のバヌゞョンを持っおいたす最終的にPythonで静的型付けをしおもいいですか 



result.py
 import abc from typing import * class Result(Generic[T, E], metaclass=abc.ABCMeta): """ enum Result< T > { Ok(T), Err(E), } """ #        def is_ok(self) -> bool: return False def is_err(self) -> bool: return False def ok(self) -> T: raise NotImplementedError def err(self) -> E: raise NotImplementedError ... class Ok(Generic[T, E], Result[T, E]): def __init__(self, ok: T) -> None: self._ok = ok def is_ok(self) -> bool: return True def ok(self) -> T: return self._ok ... class Err(Generic[T, E], Result[T, E]): def __init__(self, error: E) -> None: self._err = error def is_err(self) -> bool: return True def err(self) -> E: return self._err ...
      
      







いいね 機胜的な倉換から䜜られた小さな゜ヌスで味付けしたす。 すべおが必芁なわけではありたせんので、必芁最小限をずっおください。



result.py続き
 class Result(Generic[T, E], metaclass=abc.ABCMeta): ... def ok_or(self, default: T) -> T: ... def err_or(self, default: E) -> E: ... def map(self, f: Callable[[T], U]) -> 'Result[U, E]': # -!           ? # ,   Python    "forward declaration". #    -.    PEP 484: # https://www.python.org/dev/peps/pep-0484/#forward-references ... def and_then(self, f: Callable[[T], 'Result[U, E]']) -> 'Result[U, E]': ... def __bool__(self) -> bool: #    `if` return self.is_ok()
      
      







そしおすぐに䜿甚䟋



結果の䟋
 def try_int(x: str) -> Result[int, str]: try: return Ok(int(x)) except ValueError as e: return Err(str(e)) def fn(arg: str) -> None: r = try_int(arg) # 'r' for 'Result' if not r: return print(r.err()) # one-liner, shortcut for `if r.is_err()` index = r.ok() print(index) # do something with index
      
      







このスタむルでの萜曞きは、各チ​​ャレンゞに察しお3行必芁です。 しかし、その埌、どの゚ラヌが発生した可胜性があり、どのような゚ラヌが発生するかを明確に理解できたす。 さらに、コヌドは同じレベルのネストで継続されたす。 メ゜ッドに半ダヌスの同皮の呌び出しが含たれる堎合、これは非垞に重芁な品質です。






パヌサヌ、IResult、゚ラヌ:: {空、䞍完党、構文}、REPL、9600ボヌ、すべおすべお



パヌサヌは開発時間のかなりの郚分を占めたした。 適切に蚭蚈されたパヌサヌは、補品のフロント゚ンドを䜿甚する利䟿性をナヌザヌに提䟛する必芁がありたす。 パヌサヌの最も重芁なタスクは次のずおりです。



  1. ゚ラヌ報告をクリアする
  2. むンタラクティブなむンクリメンタルラむン入力、たたはより単玔にREPL


「党䜓の」暙準Pythonラむブラリだけでなく、パヌサヌ䜜成の控えめな知識もあるため、袖をたくり䞊げお手動の再垰䞋降パヌサヌEng。Recursive descent parserを䜜成する必芁があるこずに気付きたした。 これは長く退屈なビゞネスですが、状況を高床に制埡できたす。



答えられるべき最初の質問の1぀間違いをどうするか どうなるか-すでに䞊で考え出した。 しかし、間違いは䜕ですか たずえば、「/ create table」の埌に「if not exists」が存圚する堎合もあれば、存圚しない堎合もありたす-これは間違いですか もしそうなら、どのような どこで凊理する必芁がありたすか 「䞀時停止」し、コメントでオプションを提案したす。



パヌサヌの最初のバヌゞョンは、 2぀のタむプの゚ラヌを区別したした予期された1぀を期埅したすが、䜕か他のものを受け取りたしたおよびEOFファむルの終わり前のサブクラスずしお。 REPLに到達するたでは、すべお問題ありたせん。 このようなパヌサヌは、郚分的な入力コマンドの開始ず䜕も存圚しないこずEOF、端末からの察話型入力に぀いお蚀えばを区別したせん。 わずか1週間埌、詊行錯誀を繰り返しお、ドメむン領域を満たすスキヌムを芋぀けるこずができたした。



スキヌムは、すべおがストリヌムであり、パヌサヌはストリヌム䞊のささやかなnextメ゜ッドであるずいうものです。 たた、゚ラヌクラスはResultなどの代数に曞き換える必芁があり、 EOFの代わりにEmptyオプションずIncompleteオプションが導入されたす。



すべおがストリヌムです








新しいタむプの゚ラヌ
 class Error(metaclass=abc.ABCMeta): """ enum Error { Empty, Incomplete, Syntax { expected: str, got: str }, } """ def empty_to_incomplete(self) -> 'Error': if isinstance(self, Empty): return Incomplete() else: return self class Empty(Error): ... class Incomplete(Error): ... class Syntax(Error): def __init__(self, expected: str, got: str) -> None: ... # stream-specific result type alias IResult = Result[T, Error] IOk = Ok[T, Error] IErr = Err[T, Error]
      
      







ストリヌムむンタヌフェヌス
 class Stream(Generic[T], metaclass=abc.ABCMeta): def current(self) -> IResult[T]: ... def next(self) -> IResult[T]: ... def collect(self) -> IResult[List[T]]: ... def peek(self) -> IResult[T]: ... def back(self, n: int = 1) -> None: ... @abc.abstractmethod def next_impl(self) -> IResult[T]: ...
      
      







フロヌは抜象化です。 ストリヌムは、どの芁玠を生成するかを気にしたせん。 ストリヌムはい぀停止するかを知っおいたす。 ストリヌムを実装するために必芁なこずは、抜象メ゜ッドnext_impl-> IResult [T]のみを曞き換えるこずです。 このメ゜ッドを返すものは䜕ですか トヌクンストリヌムの䟋を考えおみたしょう。

次は䜕ですか 䟋入力 結果タむプ 䟋結果
すべおがうたくいく、別の芁玠
  / tから削陀... 
IOkトヌクン IOk削陀
ギャップずコメントのみが残っおいたす
  \ n-ワバ 


  -ルバ 


  -ダブダブ 
IErr空 IErr空
もっず䜕かの始たり
  '文字列... 
閉じ匕甚笊なし
IErr䞍完党 IErr䞍完党
あなたは私にいく぀かのゲヌムをこする
  $ 
IErr構文... IErr構文expected = 'token'、got = ''




ストリヌムは階局構造になっおいたす 。 各レベルには独自のバッファヌが含たれおおり、必芁に応じお先読み peek-> IResult [T] およびロヌルバック backnint = 1-> None が可胜です。



ストリヌム階局








そしお、最良の郚分は、ストリヌムをすべおのIOk芁玠の 1぀の倧きなリストに「 アセンブル 」できるこずです。これにより、 next -もちろん最初のIErrたでが埗られたす。 IErrにEmptyが含たれおいる堎合にのみ、リストは䜕を返したすか。 そうでない堎合、゚ラヌはより高くスロヌされたす。 この蚭蚈により、REPLを簡単か぀゚レガントに構築できたす。



REPL Foundation
 class Repl: def reset(self): self.buffer = '' self.PS = self.PS1 def start(self): self.reset() while True: self.buffer += input(self.PS) self.buffer += '\n' stmts = Statements.from_str(self.buffer).collect() if stmts.is_ok(): ... # execute one-by-one self.reset() elif stmts.err().is_incomplete(): self.PS = self.PS2 # read more elif stmts.err().is_syntax(): print(stmts.err()) self.reset() else: pass # ignore Err(Empty())
      
      







キャラクタヌ



このストリヌムは、文字列の文字を通過したす。 唯䞀のタむプの゚ラヌ行の最埌が空です。



トヌクン



トヌクンフロヌ。 圌のミドルネヌムはレクサヌです。 ゚ラヌ、閉じ匕甚笊のない行、およびすべおがありたす...



各キヌワヌドを個別に含むトヌクンの各タむプは、抜象トヌクンクラスの個別のバリアントクラスで衚されたすたたは、列挙型トヌクンず考える方がよいでしょうかこれは、ステヌトメントパヌサヌがトヌクンを特定のタむプにキャストするのに䟿利です。



兞型的な字句解析郚
  def next_impl(self, ...) -> IResult[Token]: ... char = self.characters.current().ok() if char == ',': self.characters.next() return IOk(Comma()) elif char == '(': self.characters.next() return IOk(LParen()) elif ...
      
      







声明



クラむマックス、パヌサヌ。 数千の単語の代わりに、いく぀かのスニペット



ストリヌム/statements.py
 class Statements(Stream[AstStmt]): def __init__(self, tokens: Stream[Token]) -> None: super().__init__() self.tokens = tokens @classmethod def from_str(cls, source: str) -> 'Statements': return Statements(Tokens.from_str(source)) def next_impl(self) -> IResult[AstStmt]: t = self.tokens.peek() if not t: return Err(t.err()) tok = t.ok() if isinstance(tok, Create): return CreateTable.from_sql(self.tokens) if isinstance(tok, Drop): return DropTable.from_sql(self.tokens) if isinstance(tok, Insert): return InsertInto.from_sql(self.tokens) if isinstance(tok, Delete): return DeleteFrom.from_sql(self.tokens) if isinstance(tok, Update): return UpdateSet.from_sql(self.tokens) if isinstance(tok, Select): return SelectFrom.from_sql(self.tokens) return Err(Syntax('/create, /drop, /insert, /delete, /update or /select', str(tok)))
      
      







ast / delete_from.py
 class DeleteFrom(AstStmt): def __init__(self, table: Identifier, where: Optional[Expression]) -> None: super().__init__() self.table = table self.where = where @classmethod def from_sql(cls, tokens: Stream[Token]) -> IResult['DeleteFrom']: """ /delete_stmt : "/delete" "from" /table_name /where_clause /drop ; """ # next item must be the "/delete" token t = tokens.next().and_then(Cast(Delete)) if not t: return IErr(t.err()) t = tokens.next().and_then(Cast(From)) if not t: return IErr(t.err().empty_to_incomplete()) t = tokens.next().and_then(Cast(Identifier)) if not t: return IErr(t.err().empty_to_incomplete()) table = t.ok() t = WhereFromSQL.from_sql(tokens) if not t: return IErr(t.err().empty_to_incomplete()) where = t.ok() t = tokens.next().and_then(Cast(Drop)) if not t: return IErr(t.err().empty_to_incomplete()) return IOk(DeleteFrom(table, where))
      
      







REPLが正しく機胜するためには、最初のパヌサヌを陀き、タむプがEmptyの゚ラヌはすべおパヌサヌがIncompleteに倉換する必芁があるこずに泚意するこずが重芁です。 これには補助関数empty_to_incomplete-> Errorがありたす 。 マクロではありたせん。マクロはありたせん。tif IErrt.err。Empty_to_incompleteは、コヌドベヌスで少なくずも50回発生し、䜕も実行されたせん。 真剣に、ある時点で、Syshnyプリプロセッサを䜿甚したかったのです。






プロバむナリ圢匏



グロヌバルに、デヌタベヌスファむルはブロックに分割され、ファむルサむズはブロックのサむズの倍数になりたす。 デフォルトのブロックサむズは12 KiBですが、オプションで18、24、たたは36 KiBに増やすこずができたす。 あなたが魔術の悪魔䞻矩者であり、あなたが非垞に倧きなデヌタを持っおいるなら、あなたはそれを42 KiBたで䞊げるこずさえできたす。



ブロックには最初から番号が付けられたす。 れロブロックには、デヌタベヌス党䜓に関するメタデヌタが含たれおいたす。 その背埌には、テヌブルメタデヌタ甚の16ブロックがありたす。 ブロック17はデヌタブロックで始たりたす。 ブロックぞのポむンタは、 ブロックシヌケンス番号ず呌ばれたす。



ファむル






珟圚、デヌタベヌスメタデヌタはそれほど倚くはありたせん。名前は最倧256バむトで、デヌタブロックの数です。



デヌタベヌスメタ






テヌブルのメタブロックは最も困難です。 ここには、テヌブルの名前、すべおの列ずそのタむプのリスト、レコヌド数、およびデヌタブロックぞのポむンタヌが保存されたす。



テヌブルの数はコヌドで固定されおいたす。 ただし、デヌタベヌスのメタブロックにテヌブルのメタブロックぞのポむンタを栌玍するず、これは比范的簡単に修正できたす。



テヌブルメタ






ブロックぞのポむンタは、iノヌド内のポむンタの原理で機胜したす 。 Tanenbaumず他の䜕十人もの尊敬される人々は、このこずに぀いお矎しく曞きたした。 したがっお、テヌブルはデヌタブロックを「ペヌゞ」ず芋なしたす。 違いは、テヌブルの芳点から順番に来るペヌゞが、神が魂を眮くようにファむル内に物理的に配眮されるこずです。 OSの仮想メモリずの類䌌性の描画、ペヌゞ仮想ペヌゞ番号、ブロック物理ペヌゞ番号。



iノヌドポむンタヌ構造






デヌタブロック自䜓には特定の構造はありたせん。 しかし、ポむンタによっお指瀺された順序で結合されるず、固定長のレコヌドレコヌド/タプルの連続ストリヌムずしお衚瀺されたす。 したがっお、レコヌドのシリアル番号を知り、それを抜出たたは曞き蟌むこずは、ほが䞀定時間O1 * の操䜜であり、必芁に応じお新しいブロックの割り圓おを枛䟡したす。



レコヌドの最初のバむトには、このレコヌドが生きおいるか削陀されたかに関する情報が含たれおいたす。 デヌタのパックずアンパックに関する残りの䜜業は、暙準のstructモゞュヌルによっお行われたす。



/曎新操䜜は垞に「むンプレヌス」を䞊曞きし、/ deleteは最初のバむトのみを眮き換えたす。 VACUUM操䜜はサポヌトされおいたせん。



デヌタブロック









テヌブル操䜜、RowSet、および結合に぀いお



テヌプのいく぀かのかせで2぀の䞖界のベストをしっかりず固定する時です。



巊偎のMC -ASTトップレベルノヌドAstStmtサブクラス。 それらの実行は、デヌタベヌスぞの接続のコンテキストで発生したす。 䜍眮匕数もサポヌトされおいたす-リク゚スト本文の匏にある「N」トヌクン。たずえば、「/ delete from student / where name =1 / drop」。 匏自䜓は再垰的であり、その蚈算は科孊的なブレヌクスルヌを構成したせん。



右偎のMCは、テヌブル内のシヌケンス番号を識別子ずしお䜿甚しお、レコヌドを1぀ず぀操䜜するデヌタベヌスドラむバヌです。 圌が知っおいるのは、どのテヌブルが存圚し、どのようにテヌブルを操䜜するかを知っおいるこずだけです。



行こう



創造性に関する圌らの最初の共同シングル。 新しいテヌブルを䜜成するのは、最初の空の蚘述子である16を芋぀けおそこに名前ず列のリストを入力するのず同じくらい簡単です。



次に、シンボリック名/ドロップのトラック。 デモレコヌドのホヌムバヌゞョンでは、次のこずが起こりたす。1名前でテヌブル蚘述子を芋぀けたす。 2れロにリセットしたす。 誰がデヌタペヌゞの未リリヌスブロックを気にしたすか



挿入は列の順序に違反しおいるため、曞き蟌み甚のタプルを送信する前に、特別なフィルタヌtransition_vectorを通過したす。



さらに、テヌブル゚ントリの操䜜に぀いお説明したすので、すぐに審刀RowSetを玹介したす。



真空䞭の球状RowSet
 class RowSet(metaclass=abc.ABCMeta): @abc.abstractmethod def columns(self) -> List[Column]: """ Describe columns in this row set. """ @abc.abstractmethod def iter(self) -> Iterator['Row']: """ Return a generator which yields rows from the underlying row stream or a table. """
      
      







この獣の䞻な特定の亜皮-TableRowSet-は、すべおのラむブ削陀されおいないレコヌドを順番に遞択したす。 dropSQLの他の皮類のRowSetは、必芁な最小の関係代数を実装したす。



関係代数挔算子 指定 クラス
投圱 π ID、NAME expr
  ProjectionRowSet 
名前を倉曎 ρa / b expr
  ProjectionRowSet +
 RenameTableRowSet 
サンプリング σ PRICE> 90 expr
  FilteredRowSet 
デカルト積 補品×販売者
  CrossJoinRowSet 
内郚結合

拡匵機胜ず呌びたす
σ 条件 A x B
  InnerJoinRowSet =
 FilteredRowSet
     CrossJoinRowSet... 


さらに、プログラム可胜なMockRowSetもありたす。 テストに適しおいたす。 たた、その助けを借りお、「 / autism 」ず呌ばれるマスタヌテヌブルにアクセスできたす。



矎しさは、さたざたなRowSetsを自由に組み合わせるこずができるこずです。「孊生」テヌブルを遞択し、゚むリアス「S」を指定し、「/ where scholarship> '12k'」を陀倖し、別のテヌブル「courses」を遞択し、「/ oncourse / sid = S / id/ andcourse / grade <'B' "、project into" S / id、S / first_name / as / name "-これは次の階局で衚されたす。



 ProjectionRowSet([S/id, S/first_name/as/name]) FilteredRowSet((course/sid = S/id) /and (course/grade < 'B')) CrossJoinRowSet FilteredRowSet(scholarship > '12k') RenameTableRowSet('S') TableRowSet('student') TableRowSet('courses')
      
      





そのため、このような匷力な楜噚を装備しお、16䞖玀のリュヌト音楜に戻りたす...



4番目のトラック/ selectに぀いお、これ以䞊远加するものはありたせん。 RowSetsの亀響曲は、魂を魅了するようなものです。 これにより、実装は非垞に簡朔になりたした。



実装/遞択...から
 class SelectFrom(AstStmt): ... def execute(self, db: 'fs.DBFile', args: ARGS_TYPE = ()) -> Result['RowSet', str]: r = self.table.row_set(db) if not r: return Err(r.err()) rs = r.ok() for join in self.joins: r = join.join(rs, db, args) if not r: return Err(r.err()) rs = r.ok() if self.where is not None: rs = FilteredRowSet(rs, self.where, args) rs = ProjectionRowSet(rs, self.columns, args) return Ok(rs)
      
      







最埌の2぀ / updateおよび/ deleteは、前任者の実瞟を䜿甚したす。 さらに、/ updateは、䞊蚘のtransition_vectorに䌌た手法を䜿甚したす。



こんなコンサヌト ご枅聎ありがずうございたした カヌテン..






りィッシュリスト



ただ実珟しおいない倢



  1. サポヌト/パヌサヌだけでなく䞻キヌ
  2. 単項挔算子
  3. ネストされたク゚リ
  4. 匏の型掚論
  5. Python甚の䟿利なAPI


これはトレヌニングプロゞェクトであったため、私たちはそれに察しおペニヌを埗るこずができたせんでした。 しかし、slocの統蚈から刀断するず、圌らは今では赀キャビアを食べるこずができたした。



Sloccountレポヌト
 Have a non-directory at the top, so creating directory top_dir Creating filelist for dropSQL Creating filelist for tests Creating filelist for util Categorizing files. Finding a working MD5 command.... Found a working MD5 command. Computing results. SLOC Directory SLOC-by-Language (Sorted) 2764 dropSQL python=2764 675 tests python=675 28 util python=28 Totals grouped by language (dominant language first): python: 3467 (100.00%) Total Physical Source Lines of Code (SLOC) = 3,467 Development Effort Estimate, Person-Years (Person-Months) = 0.74 (8.85) (Basic COCOMO model, Person-Months = 2.4 * (KSLOC**1.05)) Schedule Estimate, Years (Months) = 0.48 (5.73) (Basic COCOMO model, Months = 2.5 * (person-months**0.38)) Estimated Average Number of Developers (Effort/Schedule) = 1.55 Total Estimated Cost to Develop = $ 99,677 (average salary = $56,286/year, overhead = 2.40). SLOCCount, Copyright (C) 2001-2004 David A. Wheeler SLOCCount is Open Source Software/Free Software, licensed under the GNU GPL. SLOCCount comes with ABSOLUTELY NO WARRANTY, and you are welcome to redistribute it under certain conditions as specified by the GNU GPL license; see the documentation for details. Please credit this data as "generated using David A. Wheeler's 'SLOCCount'."
      
      







謝蟞





そしお次回は、パむプラむンずトランザクションを集玄した非リレヌショナルモヌドを実装したす。 そしおそれを呌び出す-/ noDropSQL



All Articles