이전 게시물에서 SQLITE 파일 형식을 조사하고 간단한 SQL 파서를 구축했습니다. 이제이 조각들을 모아 쿼리 평가자를 구현할 때입니다! 이 게시물에서는 SQL 쿼리를 평가하기위한 토대를 마련하고 기본 선택 문을 처리 할 수있는 쿼리 평가기를 작성합니다. 첫 번째 구현은 아직 필터링, 정렬, 그룹화 또는 결합을 지원하지 않지만 이러한 기능을 향후 게시물에 추가하기위한 기초를 제공합니다.

항상 그렇듯이이 게시물의 전체 소스 코드는 GitHub에서 사용할 수 있습니다.

테스트 데이터베이스를 설정하십시오

쿼리를 평가하기 전에 쿼리하려면 데이터베이스가 필요합니다. 먼저, 우리는 ID와 값이있는 단일 테이블 테이블 1 인 두 개의 열이있는 간단한 데이터베이스를 만드는 것으로 시작합니다.

sqlite3 queries_test.db sqlite> 테이블 생성 table1 (id integer, value text)> insert (id, value) 값 …> (1, 11 ‘), …> (2,’12 ‘ ), …> (3, ’13’);

⚠️ 기존 SQLITE 데이터베이스를 사용하여 쿼리를 테스트하려는 유혹을받을 수 있지만 구현은 아직 오버 플로우 페이지를 지원하지 않으므로 데이터베이스 파일에서 데이터를 읽지 못할 수 있습니다.

이 섹션은 Rust 구현에만 해당됩니다. 다른 언어로 팔로우하는 경우 안전하게 건너 뛸 수 있습니다!

현재 우리의 호출기는 독점적 인 변수 참조로만 제공됩니다. 이것은 첫 번째 유스 케이스에서는 괜찮 았지만 일단 더 복잡한 기능을 구축하기 시작하면이 제한을 유지함으로써 제약을 받게됩니다. 내부 변수 필드는 아크입니다 <_ >> 및 아크 <_ >> 포장으로 포켓을 공유 할 수 있습니다. 이를 통해 호출기를 효과적으로 복제하고 자치구 체커와의 문제를 해결하지 않고도 여러 위치에서 사용할 수 있습니다. 이 프로젝트 의이 단계에서 간단한 RC <_ >> 사용을 선택할 수 있었지만 처음부터 스레드 안전 대응 물을 사용할 것입니다. 궁극적으로 호출기에 대한 동시 액세스를 지원해야합니다.

// src/pager.rs-#[derive(Debug, Clone)]
+#[derive(Debug)]
펍 구조물 호출기 – 입력 : i, + 입력 : 아크 > page_size : usize, – 페이지 : 해시 맵 +페이지 : 아크 >>>

READ_PAGE 및 LOAD_PAGE 메소드는 그에 따라 업데이트해야합니다.

임시 휴대용 소형 무선 호출기 펍 fn read_page (& self, n : usize) -> anyaw :: result > ANYAW! ( “호출기 쓰기 잠금을 얻지 마십시오”))? LET (Page) = write_pages.get (& n) OK (page.clone ()); ; write_pages.insert (n, page.clone ()); fn load_page (& self, n : usize) -> anywaw :: result > 오프셋 = N.saturating_sub (1) * self.page_size;

read_page 메소드에 대한 두 가지 사항 :

  • 캐시에서 페이지를 읽는 첫 번째 시도는 블록에 중첩되어 읽기 잠금의 범위를 제한하고 쓰기 잠금을 획득하기 전에 해제되도록합니다.
  • 쓰기 잠금을 획득 한 후 두 개의 잠금 획득 사이에 삽입 된 경우에 페이지가 캐시에 이미 있는지 다시 확인하십시오.

마찬가지로, 쿼리 평가자에 사용할 값 열거의 소유 버전을 정의하십시오.

틀[derive(Debug, Clone)]
Pub Enum onedValue Null, String (Rc ), Blob (RC >), int (i64), float (f64), impl <'p> ~에서 <' p >>에서 (값 : 값 <'p>) -> self match value value :: null => self :: null, value :: int (i) => self :: int (i), value :: float (f) => self :: float (f) , value :: blob (b) => self :: blob (rc :: new (b.into_owned ()), value :: string (s) => self :: string (rc :: new (s.into_owned ()), inp std :: fmt :: onedvalue {fn fmt (& self, f : & mut std :: fmt :: formatter에 대한 디스플레이 <'_>) -> std :: fmt :: result matce self ronedValue :: null => 쓰기! (F, “NULL”), oneDValue :: String (s) => s. fmt (f), weldvalue :: blob (items) => & n ondervalue :: int (i) => i.fmt (f), welfalue :: float (x) => x.fmt (f),}

마지막으로 커서 구조물을 풍부하게하고 필드의 가치를 소유권 번호로 반환하는 방법을 제공합니다.

Imple Cursor Pub fn oned_field (& self, n : usize) -> 옵션 self.field (n) .map 평가 (into :: int) select 문

쿼리 엔진은 두 가지 주요 구성 요소로 구성됩니다.

  • 테이블 스캔 및 행 필터링과 같은 데이터베이스의 중첩 작업을 나타내는 반복자와 같은 연산자 열거. 첫 번째 구현에는 테이블에서 모든 행을 생성하는 Seqscan 연산자 만 포함됩니다.
  • 구문 분석 된 SQL 쿼리를 검색하고 쿼리 결과를 생성하기 위해 평가할 수있는 연산자를 생성하는 플래너 구조.

연산자의 열거를 정의하는 것으로 시작하겠습니다.

어쨌든 사용 :: 문맥. Crate :: Cursor :: Scanner, Value :: OwnedValue를 사용하십시오. 틀[derive(Debug)]
Pub Enum Operator Seqscan (Seqscan), Imple Operator Pub fn Next_row (& mut self) -> 어쨌든 :: 결과 <선택 사항<&[OwnedValue]>> self-operator :: seqscan (s) => s.next_row (),

쿼리를 평가 한 결과는 연산자의 Next_row 메소드를 반복적으로 호출하여 얻습니다. 반환 된 슬라이스의 각 값은 쿼리 결과의 열에 해당합니다.

Seqscan 구조는 테이블과 행을 스캔합니다.

틀[derive(Debug)]
Pub Struct Seqscan 필드 : Vec 스캐너 : 스캐너, Row_Buffer : Vec Imple Seqscan pub fn new (Fields : Vec 스캐너 : 스캐너) -> self let row_buffer = vec![OwnedValue::Null; fields.len()]; Selffield, 스캐너, Row_buffer, fn Next_row (& mut self) -> 어쨌든 :: 결과 <&[OwnedValue]>> lect. self.scanner.next_record ()는 OK를 반환합니다 (없음)[i] = record.owned_field (n) .context ( “발행 필드”)? OK (sess.

Seqscan 구조는 각 레코드에서 읽을 필드 인덱스 목록을 생성하는 스캐너로 초기화되고 테이블의 모든 행에 대한 레코드를 스캔합니다. 읽을 필드 수는 모든 행에 대해 동일하므로 버퍼를 Prealloce로하여 선택한 필드의 값을 저장할 수 있습니다. Next_row 메소드는 스캐너에서 다음 레코드를 검색하고 요청 된 필드 (인덱스별로 지정 됨)를 추출하고 버퍼에 저장합니다.

선택한 명령문을 평가할 연산자가 있으므로 구문 분석 된 SQL 쿼리에서 연산자를 생성하는 플래너 구조로 진행합시다.

사용 :: 보석, 문맥, OK; Super :: Operator :: Operator, Seqscan을 사용하십시오. Pub Struct Planner <'d> DB : & ‘d db, inft’d> 플래너 <' d> Pub Fn New (DB : & ‘D DB) -> Self Self DB Pub FN 컴파일 (자체, 진술 : & ast : : 문) -> anywaw :: 결과 매치 명령문 asst :: stater :: select (s) => self.compile_select (s), stmt => 보석! ( “지원되지 않은 진술 : STMT :?”),

Planner Struct는 데이터베이스를 참조하여 초기화 된 SQL 문을 검색하고 해당 연산자를 반환하는 컴파일 방법을 제공합니다. 컴파일 메소드는 각 유형의 SQL 문에 대한 특정 메소드로 발산됩니다.

선택 문을 위해 연산자를 구축하는 방법을 살펴 보겠습니다.

임프 <'d> 입안자 <' d> {fn compile_select (self, select : & ast :: selectstatement) -> anywaw :: result {selectfrom :: table (table_name) = & select.core.from; 유효하지 않은 테이블 이름 : table_name “) ??; mut columns = vec :: new (); res_col in & select.core.result_columns match res_col ast :: resultcolumn :: star => in 0..table.columns. len () columns.push (i); OK (Operator :: seqscan :: new (열, self.db.scanner (table.first_page)))}}}

먼저, select 문의 테이블 이름과 일치하는 테이블 메타 데이터 항목을 찾으십시오. 그런 다음 명령문의 결과 열을 반복하고 모든 열로 확장하거나 테이블 메타 데이터의 열 이름을 검사하여 각 레코드에서 읽을 수있는 필드 지수 목록을 만듭니다.

마지막으로 전체 탭을 스캔하고 각 행에 대해 선택한 필드를 생성하는 Seqscan 연산자를 만듭니다.

REPL 쿼리 평가

이제 테스트에 쿼리 평가자를 소개 할 시간입니다! 원시 SQL 쿼리를 읽고 평가하는 간단한 기능을 만듭니다.

fn eval_query (db : & db :: db, query : & str) -> anyaw :: result <()> parsed_query = sql :: parse_statement (query, false)? Mut Op = Engine :: Planner :: new (db) .compile (& parsed_query)? ret (value) = op.next_row () let format = value .iter (). map (tostring :: to_string) .collect :: <_ >> (). Jein ( “OK ((())))

이 기능은 파이프 라인을 만듭니다. 결과 연산자의 Next_row ()로의 반복 호출은 SQL 쿼리를 구문 분석하고 플래너에 연산자를 빌드 한 다음 결과의 각 행을 검색하고 표시합니다.

마지막 단계는이 기능을 Repl 루프에서 사용하는 것입니다.

// src/main.rs // […]

fn cli (mut db : db :: db) -> 어쨌든 :: 결과 <()> {print_flushed ( “rqlite>”)? mut line_buffer = string :: new (); 잠그다(). read_line (& mut line_buffer) .is_ok () {match line_buffer.trim () “.exit”=> break, “.tables”=> display_tables (& mut db)? , +stmt = >> eval_query (& db, stmt)? , -stmt => match sql :: parse_statement (stmt, true) -ok (stmt) => -println! ( “:?”, stmt); ( “오류 :”, e); -print_flushed ( “\ nrqlite>”) line_buffer.clear ();

이제 간단한 선택 문을 평가하기 위해 REPL을 실행할 수 있습니다.

cargolan-queries_test.db rqlite> table1에서 선택 *;

모든 것이 잘되면 다음 출력이 표시됩니다.

1 |. 11 2 |

우리의 작은 데이터베이스 엔진이 형성되기 시작했습니다! 이제 간단한 선택 쿼리를 분석하고 평가할 수 있습니다. 그러나 완전히 기능적인 데이터베이스 엔진이라고 부르기 전에 아직 다루어야 할 것이 많습니다. 다음 포스트에서는 행을 필터링하고 인덱스를 읽고 정렬 및 그룹화를 구현하는 방법을 발견합니다.

How useful was this post?

Click on a star to rate it!

Average rating 0 / 5. Vote count: 0

No votes so far! Be the first to rate this post.