Skip to content

Commit

Permalink
leetcode: Add 0511
Browse files Browse the repository at this point in the history
  • Loading branch information
XuShaohua committed Jun 1, 2024
1 parent e2184e8 commit 8c689bd
Show file tree
Hide file tree
Showing 21 changed files with 488 additions and 1 deletion.
1 change: 1 addition & 0 deletions src/leetcode/0511.game-play-analysis-i/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DATABASE_URL=postgres://leetcode:leetcode-password@localhost/leetcode
1 change: 1 addition & 0 deletions src/leetcode/0511.game-play-analysis-i/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/db
12 changes: 12 additions & 0 deletions src/leetcode/0511.game-play-analysis-i/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "lc-0511-game-play-analysis-i"
version = "0.1.0"
edition = "2021"
publish = false

[dependencies]
diesel = { version = "2.2.0", default-features = false, features = [ "chrono", "postgres", "r2d2" ] }
dotenvy = "0.15.7"
env_logger = "0.11.3"
log = "0.4.21"
r2d2 = "0.8.10"
9 changes: 9 additions & 0 deletions src/leetcode/0511.game-play-analysis-i/diesel.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# For documentation on how to configure this file,
# see https://diesel.rs/guides/configuring-diesel-cli

[print_schema]
file = "src/schema.rs"
custom_type_derives = ["diesel::query_builder::QueryId", "Clone"]

[migrations_directory]
dir = "/home/shaohua/dev/rust/TheAlgorithms/src/leetcode/0511.game-play-analysis-i/migrations"
13 changes: 13 additions & 0 deletions src/leetcode/0511.game-play-analysis-i/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
version: "3.0"
services:
leetcode_db:
image: postgres:15.3
restart: always
ports:
- 127.0.0.1:5432:5432
environment:
POSTGRES_PASSWORD: leetcode-password
POSTGRES_USER: leetcode
POSTGRES_DB: leetcode
volumes:
- ./db:/var/lib/postgresql
4 changes: 4 additions & 0 deletions src/leetcode/0511.game-play-analysis-i/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

#

[问题描述](../problems/)
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-- This file was automatically created by Diesel to setup helper functions
-- and other internal bookkeeping. This file is safe to edit, any future
-- changes will be added to existing projects as new migrations.

DROP FUNCTION IF EXISTS diesel_manage_updated_at(_tbl regclass);
DROP FUNCTION IF EXISTS diesel_set_updated_at();
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
-- This file was automatically created by Diesel to setup helper functions
-- and other internal bookkeeping. This file is safe to edit, any future
-- changes will be added to existing projects as new migrations.




-- Sets up a trigger for the given table to automatically set a column called
-- `updated_at` whenever the row is modified (unless `updated_at` was included
-- in the modified columns)
--
-- # Example
--
-- ```sql
-- CREATE TABLE users (id SERIAL PRIMARY KEY, updated_at TIMESTAMP NOT NULL DEFAULT NOW());
--
-- SELECT diesel_manage_updated_at('users');
-- ```
CREATE OR REPLACE FUNCTION diesel_manage_updated_at(_tbl regclass) RETURNS VOID AS $$
BEGIN
EXECUTE format('CREATE TRIGGER set_updated_at BEFORE UPDATE ON %s
FOR EACH ROW EXECUTE PROCEDURE diesel_set_updated_at()', _tbl);
END;
$$ LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION diesel_set_updated_at() RETURNS trigger AS $$
BEGIN
IF (
NEW IS DISTINCT FROM OLD AND
NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at
) THEN
NEW.updated_at := current_timestamp;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- This file should undo anything in `up.sql`
DROP TABLE activity;
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
-- Your SQL goes here

CREATE TABLE IF NOT EXISTS activity
(
player_id INTEGER NOT NULL,
device_id INTEGER NOT NULL,
event_date DATE NOT NULL DEFAULT CURRENT_DATE,
games_played INTEGER NOT NULL,
PRIMARY KEY (player_id, event_date)
);

INSERT INTO activity (player_id, device_id, event_date, games_played) VALUES ('1', '2', '2016-03-01', '5');
INSERT INTO activity (player_id, device_id, event_date, games_played) VALUES ('1', '2', '2016-05-02', '6');
INSERT INTO activity (player_id, device_id, event_date, games_played) VALUES ('2', '3', '2017-06-25', '1');
INSERT INTO activity (player_id, device_id, event_date, games_played) VALUES ('3', '1', '2016-03-02', '0');
INSERT INTO activity (player_id, device_id, event_date, games_played) VALUES ('3', '4', '2018-07-03', '5');
9 changes: 9 additions & 0 deletions src/leetcode/0511.game-play-analysis-i/query.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* Copyright (c) 2024 Xu Shaohua <[email protected]>. All rights reserved.
* Use of this source is governed by General Public License that can be found
* in the LICENSE file.
*/

SELECT player_id, MIN(event_date) AS first_login
FROM activity
GROUP BY player_id;
37 changes: 37 additions & 0 deletions src/leetcode/0511.game-play-analysis-i/src/db.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright (c) 2024 Xu Shaohua <[email protected]>. All rights reserved.
// Use of this source is governed by General Public License that can be found
// in the LICENSE file.

use diesel::pg::PgConnection;
use diesel::r2d2::{ConnectionManager, Pool};

use crate::error::{Error, ErrorKind};

pub type DbPool = Pool<ConnectionManager<PgConnection>>;

/// Create postgres database connection pool.
///
/// # Errors
///
/// Returns error if:
/// - No `DATABASE_URL` is set in current environment.
/// - Failed to connect to database.
pub fn get_connection_pool() -> Result<DbPool, Error> {
let url = std::env::var("DATABASE_URL").map_err(|err| {
Error::from_string(
ErrorKind::ConfigError,
format!("DATABASE_URL is not set in environment, err: {err:?}"),
)
})?;
let manager = ConnectionManager::<PgConnection>::new(&url);

Pool::builder()
.test_on_check_out(true)
.build(manager)
.map_err(|err| {
Error::from_string(
ErrorKind::DbConnError,
format!("Failed to create connection pool, url: {url}, err: {err:?}"),
)
})
}
117 changes: 117 additions & 0 deletions src/leetcode/0511.game-play-analysis-i/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// Copyright (c) 2022 Xu Shaohua <[email protected]>. All rights reserved.
// Use of this source is governed by GNU General Public License
// that can be found in the LICENSE file.

#![allow(clippy::enum_variant_names)]

use diesel::result::DatabaseErrorKind;
use std::fmt::{Display, Formatter};
use std::io;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ErrorKind {
ConfigError,

DbConnError,
DbGeneralError,
DbUniqueViolationError,
DbForeignKeyViolationError,
DbNotFoundError,

IoError,
}

unsafe impl Send for ErrorKind {}

#[derive(Debug, Clone)]
pub struct Error {
kind: ErrorKind,
message: String,
}

impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}: {}", self.kind, self.message)
}
}

impl std::error::Error for Error {}

#[allow(dead_code)]
impl Error {
#[must_use]
pub fn new(kind: ErrorKind, message: &str) -> Self {
Self {
kind,
message: message.to_owned(),
}
}

#[must_use]
pub const fn from_string(kind: ErrorKind, message: String) -> Self {
Self { kind, message }
}

#[must_use]
pub const fn kind(&self) -> ErrorKind {
self.kind
}

#[must_use]
pub fn message(&self) -> &str {
&self.message
}
}

impl From<io::Error> for Error {
fn from(err: io::Error) -> Self {
Self::from_string(ErrorKind::IoError, err.to_string())
}
}

impl From<r2d2::Error> for Error {
fn from(err: r2d2::Error) -> Self {
Self::from_string(ErrorKind::DbConnError, format!("r2d2 err: {err}"))
}
}

impl From<diesel::result::Error> for Error {
fn from(err: diesel::result::Error) -> Self {
match &err {
diesel::result::Error::DatabaseError(kind, _info) => match kind {
DatabaseErrorKind::UniqueViolation => {
Self::from_string(ErrorKind::DbUniqueViolationError, err.to_string())
}
DatabaseErrorKind::ForeignKeyViolation => {
Self::from_string(ErrorKind::DbForeignKeyViolationError, err.to_string())
}
_ => Self::from_string(ErrorKind::DbGeneralError, err.to_string()),
},
diesel::result::Error::NotFound => {
Self::from_string(ErrorKind::DbNotFoundError, err.to_string())
}
_ => Self::from_string(ErrorKind::DbGeneralError, err.to_string()),
}
}
}

impl From<std::num::ParseIntError> for Error {
fn from(err: std::num::ParseIntError) -> Self {
Self::from_string(ErrorKind::ConfigError, err.to_string())
}
}

impl From<std::ffi::OsString> for Error {
fn from(err: std::ffi::OsString) -> Self {
Self::from_string(
ErrorKind::ConfigError,
format!("OsString to String err: {err:?}"),
)
}
}

impl From<dotenvy::Error> for Error {
fn from(err: dotenvy::Error) -> Self {
Self::from_string(ErrorKind::ConfigError, format!("dotenv err: {err:?}"))
}
}
25 changes: 25 additions & 0 deletions src/leetcode/0511.game-play-analysis-i/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (c) 2024 Xu Shaohua <[email protected]>. All rights reserved.
// Use of this source is governed by General Public License that can be found
// in the LICENSE file.

use diesel::connection::SimpleConnection;
use std::env;
use std::fs;

mod db;
mod error;

use error::Error;

fn main() -> Result<(), Error> {
dotenvy::dotenv()?;
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));

let pool = db::get_connection_pool()?;
let mut conn = pool.get()?;
for arg in env::args().skip(1) {
let sql_content: String = fs::read_to_string(arg)?;
conn.batch_execute(&sql_content)?;
}
Ok(())
}
10 changes: 10 additions & 0 deletions src/leetcode/0511.game-play-analysis-i/src/schema.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// @generated automatically by Diesel CLI.

diesel::table! {
activity (player_id, event_date) {
player_id -> Int4,
device_id -> Int4,
event_date -> Date,
games_played -> Int4,
}
}
5 changes: 5 additions & 0 deletions src/leetcode/template-pgsql/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,8 @@ edition = "2021"
publish = false

[dependencies]
diesel = { version = "2.2.0", default-features = false, features = [ "chrono", "postgres", "r2d2" ] }
dotenvy = "0.15.7"
env_logger = "0.11.3"
log = "0.4.21"
r2d2 = "0.8.10"
37 changes: 37 additions & 0 deletions src/leetcode/template-pgsql/src/db.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright (c) 2024 Xu Shaohua <[email protected]>. All rights reserved.
// Use of this source is governed by General Public License that can be found
// in the LICENSE file.

use diesel::pg::PgConnection;
use diesel::r2d2::{ConnectionManager, Pool};

use crate::error::{Error, ErrorKind};

pub type DbPool = Pool<ConnectionManager<PgConnection>>;

/// Create postgres database connection pool.
///
/// # Errors
///
/// Returns error if:
/// - No `DATABASE_URL` is set in current environment.
/// - Failed to connect to database.
pub fn get_connection_pool() -> Result<DbPool, Error> {
let url = std::env::var("DATABASE_URL").map_err(|err| {
Error::from_string(
ErrorKind::ConfigError,
format!("DATABASE_URL is not set in environment, err: {err:?}"),
)
})?;
let manager = ConnectionManager::<PgConnection>::new(&url);

Pool::builder()
.test_on_check_out(true)
.build(manager)
.map_err(|err| {
Error::from_string(
ErrorKind::DbConnError,
format!("Failed to create connection pool, url: {url}, err: {err:?}"),
)
})
}
Loading

0 comments on commit 8c689bd

Please sign in to comment.