22
33"""
44from typing import Any , List , Optional
5+ from urllib import parse
56
67from sqlitecloud .driver import Driver
7- from sqlitecloud .types import SQCloudConfig , SQCloudConnect , SqliteCloudAccount
8+ from sqlitecloud .resultset import SqliteCloudResultSet
9+ from sqlitecloud .types import (
10+ SQCloudConfig ,
11+ SQCloudConnect ,
12+ SQCloudException ,
13+ SqliteCloudAccount ,
14+ )
815
916
1017class SqliteCloudClient :
1118 """
1219 Client to connect to SqliteCloud
1320 """
1421
22+ SQLITE_DEFAULT_PORT = 8860
23+
1524 def __init__ (
1625 self ,
1726 cloud_account : Optional [SqliteCloudAccount ] = None ,
@@ -28,22 +37,15 @@ def __init__(
2837
2938 """
3039 self .driver = Driver ()
31-
32- self .hostname : str = ''
33- self .port : int = 8860
34-
40+
3541 self .config = SQCloudConfig ()
3642
3743 # for pb in pub_subs:
3844 # self._pub_sub_cbs.append(("channel1", SQCloudPubSubCB(pb)))
3945 if connection_str :
40- # TODO: parse connection string to create the config
41- self .config = SQCloudConfig ()
46+ self .config = self ._parse_connection_string (connection_str )
4247 elif cloud_account :
4348 self .config .account = cloud_account
44- self .hostname = cloud_account .hostname
45- self .port = cloud_account .port
46-
4749 else :
4850 raise Exception ("Missing connection parameters" )
4951
@@ -56,7 +58,9 @@ def open_connection(self) -> SQCloudConnect:
5658 Raises:
5759 Exception: If an error occurs while opening the connection.
5860 """
59- connection = self .driver .connect (self .hostname , self .port , self .config )
61+ connection = self .driver .connect (
62+ self .config .account .hostname , self .config .account .port , self .config
63+ )
6064
6165 # SQCloudExec(connection, f"USE DATABASE {self.dbname};")
6266
@@ -75,9 +79,9 @@ def disconnect(self, conn: SQCloudConnect) -> None:
7579 """
7680 self .driver .disconnect (conn )
7781
78- # def exec_query(
79- # self, query: str, conn: SQCloudConnect = None
80- # ) -> SqliteCloudResultSet:
82+ def exec_query (
83+ self , query : str , conn : SQCloudConnect = None
84+ ) -> SqliteCloudResultSet :
8185 """Executes a SQL query on the SQLite database.
8286
8387 Args:
@@ -86,31 +90,82 @@ def disconnect(self, conn: SQCloudConnect) -> None:
8690 Returns:
8791 SqliteCloudResultSet: The result set of the executed query.
8892 """
89- # print(query)
90- # # pylint: disable=unused-variable
91- # local_conn, close_at_end = (
92- # (conn, False) if conn else (self.open_connection(), True)
93- # )
94- # result: SQCloudResult = SQCloudExec(local_conn, self._encode_str_to_c(query))
95- # self._check_connection(local_conn)
96- # return SqliteCloudResultSet(result)
97- # pass
93+ if not conn :
94+ conn = self .open_connection ()
95+
96+ result = self .driver .execute (query , conn )
97+ return SqliteCloudResultSet (result )
9898
9999 # def exec_statement(
100100 # self, query: str, values: List[Any], conn: SQCloudConnect = None
101101 # ) -> SqliteCloudResultSet:
102- # local_conn = conn if conn else self.open_connection()
103- # result: SQCloudResult = SQCloudExecArray(
104- # local_conn,
105- # self._encode_str_to_c(query),
106- # [SqlParameter(self._encode_str_to_c(str(v)), v) for v in values],
107- # )
108- # if SQCloudResultIsError(result):
109- # raise Exception(
110- # "Query error: " + str(SQCloudResultDump(local_conn, result))
111- # )
112- # return SqliteCloudResultSet(result)
113- # pass
102+ # local_conn = conn if conn else self.open_connection()
103+ # result: SQCloudResult = SQCloudExecArray(
104+ # local_conn,
105+ # self._encode_str_to_c(query),
106+ # [SqlParameter(self._encode_str_to_c(str(v)), v) for v in values],
107+ # )
108+ # if SQCloudResultIsError(result):
109+ # raise Exception(
110+ # "Query error: " + str(SQCloudResultDump(local_conn, result))
111+ # )
112+ # return SqliteCloudResultSet(result)
113+ # pass
114114
115115 def sendblob (self ):
116116 pass
117+
118+ def _parse_connection_string (self , connection_string ) -> SQCloudConfig :
119+ # URL STRING FORMAT
120+ # sqlitecloud://user:pass@host.com:port/dbname?timeout=10&key2=value2&key3=value3
121+ # or sqlitecloud://host.sqlite.cloud:8860/dbname?apikey=zIiAARzKm9XBVllbAzkB1wqrgijJ3Gx0X5z1A4m4xBA
122+
123+ config = SQCloudConfig ()
124+ config .account = SqliteCloudAccount ()
125+
126+ try :
127+ params = parse .urlparse (connection_string )
128+
129+ options = {}
130+ query = params .query
131+ options = parse .parse_qs (query )
132+ for option , values in options .items ():
133+ opt = option .lower ()
134+ value = values .pop ()
135+
136+ if value .lower () in ["true" , "false" ]:
137+ value = bool (value )
138+ elif value .isdigit ():
139+ value = int (value )
140+ else :
141+ value = value
142+
143+ if hasattr (config , opt ):
144+ setattr (config , opt , value )
145+ elif hasattr (config .account , opt ):
146+ setattr (config .account , opt , value )
147+
148+ # apikey or username/password is accepted
149+ if not config .account .apikey :
150+ config .account .username = (
151+ parse .unquote (params .username ) if params .username else ""
152+ )
153+ config .account .password = (
154+ parse .unquote (params .password ) if params .password else ""
155+ )
156+
157+ path = params .path
158+ database = path .strip ("/" )
159+ if database :
160+ config .account .database = database
161+
162+ config .account .hostname = params .hostname
163+ config .account .port = (
164+ int (params .port ) if params .port else self .SQLITE_DEFAULT_PORT
165+ )
166+
167+ return config
168+ except Exception as e :
169+ raise SQCloudException (
170+ f"Invalid connection string { connection_string } "
171+ ) from e
0 commit comments