sui_rpc_benchmark/direct/
query_template_generator.rs1use tokio_postgres::NoTls;
14use tracing::{debug, info};
15use url::Url;
16
17#[derive(Debug, Clone)]
18pub struct QueryTemplate {
19 pub query_template: String,
20 pub table_name: String,
21 pub needed_columns: Vec<String>,
22}
23
24pub struct QueryTemplateGenerator {
25 db_url: Url,
26}
27
28impl QueryTemplateGenerator {
29 pub fn new(db_url: Url) -> Self {
30 Self { db_url }
31 }
32
33 pub async fn generate_query_templates(&self) -> Result<Vec<QueryTemplate>, anyhow::Error> {
34 let (client, connection) = tokio_postgres::connect(self.db_url.as_str(), NoTls).await?;
35 tokio::spawn(connection);
36
37 let pk_query = r#"
38 SELECT
39 tc.table_name,
40 array_agg(kcu.column_name ORDER BY kcu.ordinal_position)::text[] as primary_key_columns
41 FROM information_schema.table_constraints tc
42 JOIN information_schema.key_column_usage kcu
43 ON tc.constraint_name = kcu.constraint_name
44 WHERE tc.constraint_type = 'PRIMARY KEY'
45 AND tc.table_schema = 'public'
46 AND tc.table_name != '__diesel_schema_migrations'
47 GROUP BY tc.table_name
48 ORDER BY tc.table_name;
49 "#;
50
51 let mut queries = Vec::new();
52 let rows = client.query(pk_query, &[]).await?;
53 let tables: Vec<String> = rows
54 .iter()
55 .map(|row| row.get::<_, String>("table_name"))
56 .collect::<std::collections::HashSet<_>>()
57 .into_iter()
58 .collect();
59 info!(
60 "Found {} active tables in database: {:?}",
61 tables.len(),
62 tables
63 );
64
65 for row in rows {
67 let table: String = row.get("table_name");
68 let pk_columns: Vec<String> = row.get("primary_key_columns");
69 queries.push(self.create_pk_benchmark_query(&table, &pk_columns));
70 }
71
72 let idx_query = r#"
73 SELECT
74 t.relname AS table_name,
75 i.relname AS index_name,
76 array_agg(a.attname ORDER BY k.i) AS column_names
77 FROM pg_class t
78 JOIN pg_index ix ON t.oid = ix.indrelid
79 JOIN pg_class i ON ix.indexrelid = i.oid
80 JOIN pg_attribute a ON t.oid = a.attrelid
81 JOIN generate_subscripts(ix.indkey, 1) k(i) ON a.attnum = ix.indkey[k.i]
82 WHERE t.relkind = 'r'
83 AND t.relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'public')
84 AND NOT ix.indisprimary
85 AND t.relname != '__diesel_schema_migrations'
86 GROUP BY t.relname, i.relname
87 ORDER BY t.relname, i.relname;
88 "#;
89
90 let rows = client.query(idx_query, &[]).await?;
91 for row in rows {
92 let table: String = row.get("table_name");
93 let columns: Vec<String> = row.get("column_names");
94 queries.push(self.create_index_benchmark_query(&table, &columns));
95 }
96
97 debug!("Generated {} queries:", queries.len());
98 for (i, query) in queries.iter().enumerate() {
99 debug!(
100 " {}. Table: {}, Template: {}",
101 i + 1,
102 query.table_name,
103 query.query_template
104 );
105 }
106
107 Ok(queries)
108 }
109
110 fn create_pk_benchmark_query(&self, table: &str, columns: &[String]) -> QueryTemplate {
113 let conditions = columns
114 .iter()
115 .enumerate()
116 .map(|(i, col)| format!("{} = ${}", col, i + 1))
117 .collect::<Vec<_>>()
118 .join(" AND ");
119
120 QueryTemplate {
121 query_template: format!("SELECT * FROM {} WHERE {} LIMIT 1", table, conditions),
122 table_name: table.to_string(),
123 needed_columns: columns.to_vec(),
124 }
125 }
126
127 fn create_index_benchmark_query(&self, table: &str, columns: &[String]) -> QueryTemplate {
128 let conditions = columns
129 .iter()
130 .enumerate()
131 .map(|(i, col)| format!("{} = ${}", col, i + 1))
132 .collect::<Vec<_>>()
133 .join(" AND ");
134
135 QueryTemplate {
136 query_template: format!("SELECT * FROM {} WHERE {} LIMIT 50", table, conditions),
137 table_name: table.to_string(),
138 needed_columns: columns.to_vec(),
139 }
140 }
141}