common.rs 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. use std::sync::Arc;
  2. use std::time::Duration;
  3. use rusqlite::{params, Connection};
  4. use crate::pool::{Pool, ResourceManager};
  5. /// The config need to create a new SQLite connection
  6. #[derive(Debug)]
  7. pub struct Config {
  8. path: Option<String>,
  9. password: Option<String>,
  10. }
  11. /// Sqlite connection manager
  12. #[derive(Debug)]
  13. pub struct SqliteConnectionManager;
  14. impl ResourceManager for SqliteConnectionManager {
  15. type Config = Config;
  16. type Resource = Connection;
  17. type Error = rusqlite::Error;
  18. fn new_resource(
  19. config: &Self::Config,
  20. ) -> Result<Self::Resource, crate::pool::Error<Self::Error>> {
  21. let conn = if let Some(path) = config.path.as_ref() {
  22. Connection::open(path)?
  23. } else {
  24. Connection::open_in_memory()?
  25. };
  26. if let Some(password) = config.password.as_ref() {
  27. conn.execute_batch(&format!("pragma key = '{password}';"))?;
  28. }
  29. conn.execute_batch(
  30. r#"
  31. pragma busy_timeout = 10000;
  32. pragma journal_mode = WAL;
  33. pragma synchronous = normal;
  34. pragma temp_store = memory;
  35. pragma mmap_size = 30000000000;
  36. pragma cache = shared;
  37. "#,
  38. )?;
  39. Ok(conn)
  40. }
  41. }
  42. /// Create a configured rusqlite connection to a SQLite database.
  43. /// For SQLCipher support, enable the "sqlcipher" feature and pass a password.
  44. pub fn create_sqlite_pool(
  45. path: &str,
  46. #[cfg(feature = "sqlcipher")] password: String,
  47. ) -> Arc<Pool<SqliteConnectionManager>> {
  48. #[cfg(feature = "sqlcipher")]
  49. let password = Some(password);
  50. #[cfg(not(feature = "sqlcipher"))]
  51. let password = None;
  52. let (config, max_size) = if path.contains(":memory:") {
  53. (
  54. Config {
  55. path: None,
  56. password,
  57. },
  58. 1,
  59. )
  60. } else {
  61. (
  62. Config {
  63. path: Some(path.to_owned()),
  64. password,
  65. },
  66. 20,
  67. )
  68. };
  69. Pool::new(config, max_size, Duration::from_secs(5))
  70. }
  71. /// Migrates the migration generated by `build.rs`
  72. pub fn migrate(conn: &mut Connection, migrations: &[(&str, &str)]) -> Result<(), rusqlite::Error> {
  73. let tx = conn.transaction()?;
  74. tx.execute(
  75. r#"
  76. CREATE TABLE IF NOT EXISTS migrations (
  77. name TEXT PRIMARY KEY,
  78. applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
  79. )
  80. "#,
  81. [],
  82. )?;
  83. if tx.query_row(
  84. r#"select count(*) from sqlite_master where name = '_sqlx_migrations'"#,
  85. [],
  86. |row| row.get::<_, i32>(0),
  87. )? == 1
  88. {
  89. tx.execute_batch(
  90. r#"
  91. INSERT INTO migrations
  92. SELECT
  93. version || '_' || REPLACE(description, ' ', '_') || '.sql',
  94. execution_time
  95. FROM _sqlx_migrations;
  96. DROP TABLE _sqlx_migrations;
  97. "#,
  98. )?;
  99. }
  100. // Apply each migration if it hasn’t been applied yet
  101. for (name, sql) in migrations {
  102. let already_applied: bool = tx.query_row(
  103. "SELECT EXISTS(SELECT 1 FROM migrations WHERE name = ?1)",
  104. params![name],
  105. |row| row.get(0),
  106. )?;
  107. if !already_applied {
  108. tx.execute_batch(sql)?;
  109. tx.execute("INSERT INTO migrations (name) VALUES (?1)", params![name])?;
  110. }
  111. }
  112. tx.commit()?;
  113. Ok(())
  114. }