PostgreSQLClient.gd 65 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911
  1. # Lience MIT
  2. # Written by Samuel MARZIN
  3. # Detailed documentation: https://github.com/Marzin-bot/PostgreSQLClient/wiki/Documentation
  4. extends Object
  5. ## Godot PostgreSQL Client is a GDscript script/class that allows you to connect to a Postgres backend and run SQL commands there.
  6. ## It is able to send data and receive it from the backend. Useful for managing player user data on a multiplayer game, by saving a large amount of data on a dedicated Postgres server from GDscript.
  7. ## The class is written in pure GDScript which allows it not to depend on GDNative. This makes it ultra portable for many platforms.
  8. class_name PostgreSQLClient
  9. ## Version number (minor.major) of the PostgreSQL protocol used when connecting to the backend.
  10. const PROTOCOL_VERSION := 3.0
  11. ## Backend runtime parameters
  12. ## A dictionary that contains various information about the state of the server.
  13. ## For security reasons the dictionary is always empty if the frontend is disconnected from the backend and updates once the connection is established.
  14. var parameter_status := {}
  15. ## Enemeration the statuts of the connection.
  16. enum Status {
  17. STATUS_DISCONNECTED, ## A status representing a PostgreSQLClient that is disconnected.
  18. STATUS_CONNECTING, ## A status representing a PostgreSQLClient that is connecting to a host.
  19. STATUS_CONNECTED, ## A status representing a PostgreSQLClient that is connected to a host.
  20. STATUS_ERROR ## A status representing a PostgreSQLClient in error state.
  21. }
  22. # The statut of the connection.
  23. var status = Status.STATUS_DISCONNECTED setget set_status, get_status
  24. ## Returns the status of the connection (see the Status enumeration).
  25. func get_status() -> int:
  26. return status
  27. func set_status(_value) -> void:
  28. # The value of the "status" variable can only be modified locally.
  29. pass
  30. var password_global: String
  31. var user_global: String
  32. var client := StreamPeerTCP.new()
  33. var peerstream := PacketPeerStream.new()
  34. var stream_peer_ssl = StreamPeerSSL.new()
  35. var peer: StreamPeer
  36. func _init() -> void:
  37. peerstream.set_stream_peer(client)
  38. peer = peerstream.stream_peer
  39. ## Fires when the connection to the backend closes.
  40. ## was_clean_close is true if the connection was closed correctly otherwise false.
  41. signal connection_closed(was_clean_close)
  42. # No use
  43. #signal connection_error() # del /!\
  44. ## Triggered when the authentication process failed during contact with the target backend.
  45. ## The error_object parameter is a dictionary that contains various information during the nature of the error.
  46. signal authentication_error(error_object)
  47. ## Trigger when the connection between the frontend and the backend is established.
  48. ## This is usually a good time to start making requests to the backend with execute ().
  49. signal connection_established
  50. #signal data_received
  51. ################## No use at the moment ###############
  52. ## The process ID of this backend.
  53. var process_backend_id: int
  54. ################## No use at the moment ###############
  55. ## The secret key of this backend.
  56. var process_backend_secret_key: int
  57. var status_ssl = 0
  58. var global_url = ""
  59. var startup_message: PoolByteArray
  60. var next_etape := false
  61. var con_ssl: bool
  62. ## Allows you to connect to a Postgresql backend at the specified url.
  63. func connect_to_host(url: String, ssl := false, _connect_timeout := 30) -> int:
  64. global_url = url
  65. con_ssl = ssl
  66. var error := 1
  67. # If the fontend was already connected to the backend, we disconnect it before reconnecting.
  68. if status == Status.STATUS_CONNECTED:
  69. close(false)
  70. var regex = RegEx.new()
  71. # https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING
  72. regex.compile("^(?:postgresql|postgres)://(.+):(.+)@(.+):(\\d*)/(.+)")
  73. var result = regex.search(url)
  74. if result:
  75. ### StartupMessage ###
  76. # "postgres" is the database and user by default.
  77. startup_message = request("", "user".to_ascii() + PoolByteArray([0]) + result.strings[1].to_utf8() + PoolByteArray([0]) + "database".to_ascii() + PoolByteArray([0]) + result.strings[5].to_utf8() + PoolByteArray([0, 0]))
  78. password_global = result.strings[2]
  79. user_global = result.strings[1]
  80. # The default port for postgresql.
  81. var port = 5432
  82. if result.strings[4]:
  83. port = int(result.strings[4])
  84. if stream_peer_ssl.get_status() == stream_peer_ssl.STATUS_CONNECTED:
  85. stream_peer_ssl.put_data(startup_message)
  86. else:
  87. if not client.is_connected_to_host() and client.get_status() == StreamPeerTCP.STATUS_NONE:
  88. error = client.connect_to_host(result.strings[3], port)
  89. #if (error == OK)
  90. # Get the fist message of server.
  91. if error == OK:
  92. next_etape = true
  93. else:
  94. print("[PostgreSQLClient:%d] Invalid host Postgres." % [get_instance_id()])
  95. else:
  96. status = Status.STATUS_ERROR
  97. push_error("[PostgreSQLClient:%d] Invalid Postgres URL." % [get_instance_id()])
  98. return error
  99. ## A dictionary which contains various information on the execution errors of the last requests made on the backend (usually after using the execute() method).
  100. ## If the dictionary is empty, it means that the backend did not detect any error in the query.
  101. ## Should be used ideally after each use of the execute() method.
  102. ## For security reasons, the dictionary is empty when the frontend is not connected to the backend.
  103. var error_object := {}
  104. ## Allows you to close the connection with the backend.
  105. ## If clean_closure is true, the frontend will notify the backend that it requests to close the connection.
  106. ## If false, the frontend forcibly closes the connection without notifying the backend (not recommended sof in exceptional cases).
  107. ## Has no effect if the frontend is not already connected to the backend.
  108. func close(clean_closure := true) -> void:
  109. if status == Status.STATUS_CONNECTED:
  110. ### Terminate ###
  111. # Identifies the message as a termination.
  112. if stream_peer_ssl.get_status() == stream_peer_ssl.STATUS_HANDSHAKING or stream_peer_ssl.get_status() == stream_peer_ssl.STATUS_CONNECTED:
  113. # Deconnection ssl
  114. if clean_closure:
  115. stream_peer_ssl.put_data(request('X', PoolByteArray()))
  116. stream_peer_ssl.disconnect_from_stream()
  117. else:
  118. if clean_closure:
  119. var _unused = peer.put_data(request('X', PoolByteArray()))
  120. client.disconnect_from_host()
  121. # For security reasons, the dictionary is empty when the frontend is not connected to the backend.
  122. parameter_status = {}
  123. # For security reasons, the dictionary is empty when the frontend is not connected to the backend.
  124. error_object = {}
  125. status = Status.STATUS_DISCONNECTED
  126. next_etape = false
  127. status_ssl = 0
  128. emit_signal("connection_closed", clean_closure)
  129. else:
  130. push_warning("[PostgreSQLClient:%d] The fontend was already disconnected from the backend when calling close()." % [get_instance_id()])
  131. ## Allows to send an SQL string to the backend that should run.
  132. ## The sql parameter can contain one or more valid SQL statements.
  133. ## Returns an Array of PostgreSQLQueryResult. (Can be empty)
  134. ## There are as many PostgreSQLQueryResult elements in the array as there are SQL statements in sql (sof in exceptional cases).
  135. func execute(sql: String) -> Array:
  136. if status == Status.STATUS_CONNECTED:
  137. var _unused
  138. var request := request('Q', sql.to_utf8() + PoolByteArray([0]))
  139. if stream_peer_ssl.get_status() == stream_peer_ssl.STATUS_CONNECTED:
  140. stream_peer_ssl.put_data(request)
  141. else:
  142. _unused = peer.put_data(request)
  143. while client.is_connected_to_host() and client.get_status() == StreamPeerTCP.STATUS_CONNECTED and status == Status.STATUS_CONNECTED:
  144. var reponce := [OK, PoolByteArray()]
  145. if stream_peer_ssl.get_status() == stream_peer_ssl.STATUS_CONNECTED:
  146. stream_peer_ssl.poll()
  147. if stream_peer_ssl.get_available_bytes():
  148. reponce = stream_peer_ssl.get_data(stream_peer_ssl.get_available_bytes()) # I don't know why it crashes when this value (stream_peer_ssl.get_available_bytes()) is equal to 0 so I pass it a condition. It is probably a Godot bug.
  149. else:
  150. continue
  151. else:
  152. reponce = peer.get_data(peer.get_available_bytes())
  153. if reponce[0] == OK:
  154. var result = reponce_parser(reponce[1])
  155. if result != null:
  156. return result
  157. else:
  158. push_warning("[PostgreSQLClient:%d] The backend did not send any data or there must have been a problem while the backend sent a response to the request." % [get_instance_id()])
  159. else:
  160. push_error("[PostgreSQLClient:%d] The frontend is not connected to backend." % [get_instance_id()])
  161. return []
  162. ## Upgrade the connexion to SSL.
  163. func set_ssl_connection() -> void:
  164. var _unused
  165. if stream_peer_ssl.get_status() == StreamPeerSSL.STATUS_HANDSHAKING or stream_peer_ssl.get_status() == StreamPeerSSL.STATUS_CONNECTED:
  166. push_warning("[PostgreSQLClient:%d] The connection is already secured with TLS/SSL." % [get_instance_id()])
  167. elif client.get_status() == StreamPeerTCP.STATUS_CONNECTED:
  168. ### SSLRequest ###
  169. var buffer := StreamPeerBuffer.new()
  170. # Length of message contents in bytes, including self.
  171. _unused = buffer.put_data(get_32byte_invert(8, true))
  172. # The SSL request code.
  173. # The value is chosen to contain 1234 in the most significant 16 bits, and 5679 in the least significant 16 bits. (To avoid confusion, this code must not be the same as any protocol version number.)
  174. _unused = buffer.put_data(get_32byte_invert(80877103))
  175. _unused = peer.put_data(buffer.data_array)
  176. status_ssl = 1
  177. else:
  178. push_error("[PostgreSQLClient:%d] The frontend is not connected to backend." % [get_instance_id()])
  179. ##### No use #####
  180. ## Upgrade the connexion to GSSAPI.
  181. func set_gssapi_connection() -> void:
  182. var _unused
  183. if client.get_status() == StreamPeerTCP.STATUS_CONNECTED:
  184. ### GSSENCRequest ###
  185. var buffer := StreamPeerBuffer.new()
  186. # Length of message contents in bytes, including self.
  187. _unused = buffer.put_data(get_32byte_invert(8, true))
  188. # The GSSAPI Encryption request code.
  189. # The value is chosen to contain 1234 in the most significant 16 bits, and 5680 in the least significant 16 bits. (To avoid confusion, this code must not be the same as any protocol version number.)
  190. _unused = buffer.put_data(get_32byte_invert(80877104))
  191. _unused = peer.put_data(buffer.data_array)
  192. else:
  193. push_error("[PostgreSQLClient:%d] The frontend is not connected to backend." % [get_instance_id()])
  194. ## This function undoes all changes made to the database since the last Commit.
  195. func rollback(process_id: int, process_key: int) -> void:
  196. ### CancelRequest ###
  197. var _unused
  198. if status == Status.STATUS_CONNECTED:
  199. var buffer := StreamPeerBuffer.new()
  200. # Length of message contents in bytes, including self.
  201. buffer.put_u32(16)
  202. var message_length := buffer.data_array
  203. message_length.invert()
  204. _unused = buffer.put_data(message_length)
  205. # The cancel request code.
  206. # The value is chosen to contain 1234 in the most significant 16 bits, and 5678 in the least 16 significant bits. (To avoid confusion, this code must not be the same as any protocol version number.)
  207. _unused = buffer.put_data(get_32byte_invert(80877102))
  208. # The process ID of the target backend.
  209. buffer.put_u32(process_id)
  210. # The secret key for the target backend.
  211. buffer.put_u32(process_key)
  212. _unused = peer.put_data(buffer.data_array.subarray(4, -1))
  213. else:
  214. push_error("[PostgreSQLClient:%d] The frontend is not connected to backend." % [get_instance_id()])
  215. ## Poll the connection to check for incoming messages.
  216. ## Ideally, it should be called before PostgreSQLClient.execute() for it to work properly and called frequently in a loop.
  217. func poll() -> void:
  218. var _unused
  219. if stream_peer_ssl.get_status() == stream_peer_ssl.STATUS_HANDSHAKING or stream_peer_ssl.get_status() == stream_peer_ssl.STATUS_CONNECTED:
  220. stream_peer_ssl.poll()
  221. if client.is_connected_to_host():
  222. if client.get_status() == StreamPeerTCP.STATUS_CONNECTED:
  223. if next_etape:
  224. if con_ssl:
  225. ### SSLRequest ###
  226. set_ssl_connection()
  227. else:
  228. _unused = peer.put_data(startup_message)
  229. startup_message = PoolByteArray()
  230. next_etape = false
  231. if status_ssl == 1:
  232. var response = peer.get_data(peer.get_available_bytes())
  233. if response[0] == OK:
  234. if not response[1].empty():
  235. match char(response[1][0]):
  236. 'S':
  237. #var crypto = Crypto.new()
  238. #var ssl_key = crypto.generate_rsa(4096)
  239. #var ssl_cert = crypto.generate_self_signed_certificate(ssl_key)
  240. stream_peer_ssl.connect_to_stream(peer)
  241. # stream_peer_ssl.blocking_handshake = false
  242. status_ssl = 2
  243. 'N':
  244. status = Status.STATUS_ERROR
  245. push_error("[PostgreSQLClient:%d] The connection attempt failed. The backend does not want to establish a secure SSL/TLS connection." % [get_instance_id()])
  246. close(false)
  247. var value:
  248. status = Status.STATUS_ERROR
  249. push_error("[PostgreSQLClient:%d] The backend sent an unknown response to the request to establish a secure connection. Response is not recognized: '%c'." % [get_instance_id(), value])
  250. close(false)
  251. else:
  252. push_warning("[PostgreSQLClient:%d] The backend did not send any data or there must have been a problem while the backend sent a response to the request." % [get_instance_id()])
  253. if status_ssl == 2 and stream_peer_ssl.get_status() == stream_peer_ssl.STATUS_CONNECTED:
  254. _unused = connect_to_host(global_url, false)
  255. status_ssl = 3
  256. if status_ssl != 1 and status_ssl != 2 and not status == Status.STATUS_CONNECTED and client.get_status() == StreamPeerTCP.STATUS_CONNECTED:
  257. var reponce: Array
  258. if status_ssl == 0:
  259. reponce = peer.get_data(peer.get_available_bytes())
  260. else:
  261. reponce = stream_peer_ssl.get_data(stream_peer_ssl.get_available_bytes())
  262. if reponce[0] == OK and reponce[1].size():
  263. var servire = reponce_parser(reponce[1])
  264. if servire:
  265. if status_ssl == 0:
  266. _unused = peer.put_data(servire)
  267. else:
  268. stream_peer_ssl.put_data(servire)
  269. func request(type_message: String, message := PoolByteArray()) -> PoolByteArray:
  270. var _unused
  271. # Get the size of message.
  272. var buffer := StreamPeerBuffer.new()
  273. buffer.put_u32(message.size() + (4 if type_message else 8))
  274. var message_length := buffer.data_array
  275. message_length.invert()
  276. # If the message is not StartupMessage...
  277. if type_message:
  278. buffer.put_u8(ord(type_message))
  279. _unused = buffer.put_data(message_length)
  280. # If the message is StartupMessage...
  281. if not type_message:
  282. # Version parsing
  283. var protocol_major_version = int(PROTOCOL_VERSION)
  284. var protocol_minor_version = protocol_major_version - PROTOCOL_VERSION
  285. for char_number in str(protocol_major_version).pad_zeros(2) + str(protocol_minor_version).pad_zeros(2):
  286. _unused = buffer.put_data(PoolByteArray([int(char_number)]))
  287. _unused = buffer.put_data(message)
  288. error_object = {}
  289. return buffer.data_array.subarray(4, -1)
  290. static func get_32byte_invert(integer: int, unsigned := false) -> PoolByteArray:
  291. var buffer := StreamPeerBuffer.new()
  292. if unsigned:
  293. buffer.put_u32(integer)
  294. else:
  295. buffer.put_32(integer)
  296. var bytes := buffer.data_array
  297. bytes.invert()
  298. return bytes
  299. static func split_pool_byte_array(pool_byte_array: PoolByteArray, delimiter: int) -> Array:
  300. var array := []
  301. var from := 0
  302. var to := 0
  303. for byte in pool_byte_array:
  304. if byte == delimiter:
  305. array.append(pool_byte_array.subarray(from, to))
  306. from = to + 1
  307. to += 1
  308. return array
  309. static func pbkdf2(hash_type: int, password: PoolByteArray, salt: PoolByteArray, iterations := 4096, length := 0) -> PoolByteArray:
  310. var crypto := Crypto.new()
  311. var hash_length := len(crypto.hmac_digest(hash_type, salt, password))
  312. if length == 0:
  313. length = hash_length
  314. var output := PoolByteArray()
  315. var block_count := ceil(float(length) / hash_length)
  316. var buffer := PoolByteArray()
  317. buffer.resize(4)
  318. var block := 1
  319. while block <= block_count:
  320. buffer[0] = (block >> 24) & 0xFF
  321. buffer[1] = (block >> 16) & 0xFF
  322. buffer[2] = (block >> 8) & 0xFF
  323. buffer[3] = block & 0xFF
  324. var key_1 := crypto.hmac_digest(hash_type, password, salt + buffer)
  325. var key_2 := key_1
  326. for _index in iterations - 1:
  327. key_1 = crypto.hmac_digest(hash_type, password, key_1)
  328. for index in key_1.size():
  329. key_2[index] ^= key_1[index]
  330. output += key_2
  331. block += 1
  332. return output.subarray(0, hash_length - 1)
  333. enum DataTypePostgreSQL {
  334. BOOLEAN = 16,
  335. SMALLINT = 21,
  336. INTEGER = 23,
  337. BIGINT = 20,
  338. REAL = 700,
  339. DOUBLE_PRECISION = 701,
  340. TEXT = 25,
  341. CHARACTER = 1042, # Alias CHAR.
  342. CHARACTER_VARYING = 1043, # Alias VARCHAR.
  343. JSON = 114,
  344. JSONB = 3802,
  345. XML = 142,
  346. BITEA = 17,
  347. CIDR = 650,
  348. INET = 869,
  349. MACADDR = 829,
  350. MACADDR8 = 774,
  351. BIT = 1560,
  352. BIT_VARYING = 1562,
  353. UUID = 2950,
  354. POINT = 600,
  355. BOX = 603,
  356. LSEG = 601,
  357. LINE = 628,
  358. CIRCLE = 718,
  359. DATE = 1082,
  360. TIME = 1266
  361. }
  362. ## The PostgreSQLQueryResult class is a subclass of PostgreSQLClient which is not intended to be created manually.
  363. ## It represents the result of an SQL query and provides an information and method report to use the result of the query.
  364. ## It is usually returned by the PostgreSQLClient.execute() method in an array of PostgreSQLQueryResult.
  365. class PostgreSQLQueryResult:
  366. ## Specifies the number of fields in a row (can be zero).
  367. var number_of_fields_in_a_row := 0
  368. ## An array that contains dictionaries.
  369. ## These dictionaries represent the description of the rows where the query was executed.
  370. ## The number of dictionary depends on the number of fields resulting from the result of the query which was executed.
  371. var row_description := []
  372. ## An Array that contains sub-arrays.
  373. ## These sub-arrays represent for most of the queries the rows of the table where the query was executed.
  374. ## The number of sub-tables depends on the query that has been made.
  375. ## These sub-arrays contain as many elements as number_of_fields_in_a_row.
  376. ## These elements are native GDscript types that represent the data resulting from the query.
  377. var data_row := []
  378. ## An Array that contains sub-arrays.
  379. ## These sub-arrays represent for most of the queries the rows of the table where the query was executed.
  380. ## The number of sub-tables depends on the query that has been made.
  381. ## These sub-arrays contain as many elements as number_of_fields_in_a_row.
  382. var raw_data_row := []
  383. ## This is usually a single word that identifies which SQL command was completed.
  384. var command_tag: String
  385. ## Represents various information about the execution status of the query notified by the backend. Can be empty.
  386. var notice := {}
  387. ## Returns all the values of a field.
  388. ## field_name is the name of the field on which we get the values.
  389. ## Can be empty if the field name is unknown.
  390. ## The field_name parameter is case sensitive.
  391. func get_field_values(field_name: String) -> Array:
  392. var values := []
  393. var fields_index: int
  394. for i in number_of_fields_in_a_row:
  395. if row_description[i]["field_name"] == field_name:
  396. fields_index = i
  397. break
  398. if fields_index == null:
  399. return values
  400. for data in data_row:
  401. values.append(data[fields_index])
  402. return values
  403. ## Returns the object ID of the data type of the field.
  404. ## field_name is the name of the field whose type we get.
  405. ## Can return -1 if the field name is unknown.
  406. ## The field_name parameter is case sensitive.
  407. func field_data_type(field_name: String) -> int:
  408. for i in number_of_fields_in_a_row:
  409. if row_description[i]["field_name"] == field_name:
  410. return row_description[i]["type_object_id"]
  411. return -1
  412. var postgresql_query_result_instance := PostgreSQLQueryResult.new()
  413. var datas_command_sql := []
  414. var response_buffer: PoolByteArray
  415. var client_first_message: String # Authentication SASL
  416. var salted_password: PoolByteArray # Authentication SASL
  417. var auth_message: String # Authentication SASL
  418. func reponce_parser(response: PoolByteArray):
  419. var _unused
  420. response_buffer += response
  421. while response_buffer.size() > 4:
  422. # Get the length of the response.
  423. var data_length = response_buffer.subarray(1, 4)
  424. data_length.invert()
  425. var buffer := StreamPeerBuffer.new()
  426. _unused = buffer.put_data(data_length)
  427. buffer.seek(0)
  428. # Message length.
  429. var message_length = buffer.get_u32()
  430. # If the size of the buffer is not equal to the length of the message, the request is not processed immediately.
  431. # The server may send a fragmented response.
  432. # We must therefore wait to receive the full response.
  433. if response_buffer.size() < message_length + 1:
  434. break
  435. # Message type.
  436. match char(response_buffer[0]):
  437. 'A':
  438. ### NotificationResponse ###
  439. # Get the process ID of the notifying backend process.
  440. var process_id = response_buffer.subarray(5, 8)
  441. process_id.invert()
  442. _unused = buffer.put_data(process_id)
  443. buffer.seek(4)
  444. process_id = buffer.get_32()
  445. # We get the following parameters.
  446. var situation_report_data := split_pool_byte_array(response_buffer.subarray(5, message_length), 0)
  447. # Get the name of the channel that the notify has been raised on.
  448. var name_of_channel: String = situation_report_data[0].get_string_from_utf8()
  449. # Get the "payload" string passed from the notifying process.
  450. var payload: String = situation_report_data[1].get_string_from_utf8()
  451. # The result.
  452. prints(process_id, name_of_channel, payload)
  453. 'C':
  454. ### CommandComplete ###
  455. # Identifies the message as a command-completed response.
  456. # Get the command tag. This is usually a single word that identifies which SQL command was completed.
  457. var command_tag = response_buffer.subarray(5, message_length).get_string_from_ascii()
  458. # The result.
  459. postgresql_query_result_instance.command_tag = command_tag
  460. datas_command_sql.append(postgresql_query_result_instance)
  461. # Now is a good time to create a new return object for a possible next request.
  462. postgresql_query_result_instance = PostgreSQLQueryResult.new()
  463. 'D':
  464. ### DataRow ###
  465. # Identifies the message as a data row.
  466. # Number of column values ​​that follow (can be zero).
  467. var number_of_columns = response_buffer.subarray(5, 6)
  468. number_of_columns.invert()
  469. _unused = buffer.put_data(number_of_columns)
  470. buffer.seek(4)
  471. number_of_columns = buffer.get_16()
  472. var cursor := 0
  473. var row := []
  474. var raw_row := []
  475. # Next, the following pair of fields appear for each column.
  476. for i in number_of_columns:
  477. var value_length = response_buffer.subarray(cursor + 7, cursor + 10)
  478. value_length.invert()
  479. buffer = StreamPeerBuffer.new()
  480. _unused = buffer.put_data(value_length)
  481. buffer.seek(0)
  482. value_length = buffer.get_32()
  483. if value_length == -1:
  484. ### NULL ###
  485. # The result.
  486. row.append(null)
  487. match postgresql_query_result_instance.row_description[i].format_code:
  488. 0:
  489. raw_row.append("")
  490. 1:
  491. raw_row.append(PoolByteArray())
  492. _:
  493. print("error")
  494. value_length = 0
  495. else:
  496. var value_data := response_buffer.subarray(cursor + 11, cursor + value_length + 10)
  497. match postgresql_query_result_instance.row_description[i].format_code:
  498. 0:
  499. raw_row.append(value_data.get_string_from_ascii())
  500. 1:
  501. raw_row.append(value_data)
  502. _:
  503. print("error")
  504. var error: int
  505. match postgresql_query_result_instance.row_description[i].type_object_id:
  506. DataTypePostgreSQL.BOOLEAN:
  507. ### BOOLEAN ###
  508. # The type returned is bool.
  509. match char(value_data[0]):
  510. 't':
  511. ### TRUE ###
  512. # The result.
  513. row.append(true)
  514. 'f':
  515. ### FALSE ###
  516. # The result.
  517. row.append(false)
  518. var value_column:
  519. push_error("[PostgreSQLClient:%d] The backend sent an invalid BOOLEAN object. Column value is not recognized: '%c'." % [get_instance_id(), value_column])
  520. close(false)
  521. return
  522. DataTypePostgreSQL.SMALLINT:
  523. ### SMALLINT ###
  524. # The type returned is int.
  525. # The result.
  526. row.append(int(value_data.get_string_from_ascii()))
  527. DataTypePostgreSQL.INTEGER:
  528. ### INTEGER ###
  529. # The type returned is int.
  530. # The result.
  531. row.append(int(value_data.get_string_from_ascii()))
  532. DataTypePostgreSQL.BIGINT:
  533. ### BIGINT ###
  534. # The type returned is int.
  535. # The result.
  536. row.append(int(value_data.get_string_from_ascii()))
  537. DataTypePostgreSQL.REAL:
  538. ### REAL ###
  539. # The type returned is float.
  540. # The result.
  541. row.append(float(value_data.get_string_from_ascii()))
  542. DataTypePostgreSQL.DOUBLE_PRECISION:
  543. ### DOUBLE PRECISION ###
  544. # The type returned is float.
  545. # The result.
  546. row.append(float(value_data.get_string_from_ascii()))
  547. DataTypePostgreSQL.TEXT:
  548. ### TEXT ###
  549. # The type returned is String.
  550. # The result.
  551. row.append(value_data.get_string_from_utf8())
  552. DataTypePostgreSQL.CHARACTER:
  553. ### CHARACTER ###
  554. # The type returned is String.
  555. # The result.
  556. row.append(value_data.get_string_from_utf8())
  557. DataTypePostgreSQL.CHARACTER_VARYING:
  558. ### CHARACTER_VARYING ###
  559. # The type returned is String.
  560. # The result.
  561. row.append(value_data.get_string_from_utf8())
  562. "tsvector":
  563. ### TSVECTOR ###
  564. pass
  565. "tsquery":
  566. ### TSQUERY ###
  567. pass
  568. DataTypePostgreSQL.XML:
  569. ### XML ###
  570. # The type returned is String.
  571. var xml := XMLParser.new()
  572. error = xml.open_buffer(value_data)
  573. if error == OK:
  574. # The result.
  575. row.append(value_data.get_string_from_utf8())
  576. else:
  577. push_error("[PostgreSQLClient:%d] The backend sent an invalid XML object. (Error: %d)" % [get_instance_id(), error])
  578. close(false)
  579. response_buffer = PoolByteArray()
  580. return
  581. DataTypePostgreSQL.JSON:
  582. ### JSON ###
  583. # The type returned is String.
  584. var json = value_data.get_string_from_utf8()
  585. var json_error := validate_json(json)
  586. if json_error:
  587. push_error("[PostgreSQLClient:%d] The backend sent an invalid JSON object: (Error: %d)" % [get_instance_id(), json_error])
  588. close(false)
  589. response_buffer = PoolByteArray()
  590. return
  591. else:
  592. # The result.
  593. row.append(json)
  594. DataTypePostgreSQL.JSONB:
  595. ### JSONB ###
  596. # The type returned is String.
  597. var json = value_data.get_string_from_utf8()
  598. var json_error := validate_json(json)
  599. if json_error:
  600. push_error("[PostgreSQLClient:%d] The backend sent an invalid JSONB object: (Error: %d)" % [get_instance_id(), json_error])
  601. close(false)
  602. response_buffer = PoolByteArray()
  603. return
  604. else:
  605. # The result.
  606. row.append(json)
  607. DataTypePostgreSQL.BIT:
  608. ### BIT ###
  609. # The type returned is String.
  610. # Ideally we should validate the value sent by the backend...
  611. # The result.
  612. row.append(value_data.get_string_from_ascii())
  613. DataTypePostgreSQL.BIT_VARYING:
  614. ### BIT VARYING ###
  615. # The type returned is String.
  616. # Ideally we should validate the value sent by the backend...
  617. # The result.
  618. row.append(value_data.get_string_from_ascii())
  619. DataTypePostgreSQL.BITEA:
  620. ### BITEA ###
  621. # /!\ Support not complet (not end). /!\
  622. # The type returned is PoolByteArray.
  623. var bitea_data := value_data.get_string_from_ascii()
  624. if bitea_data.substr(2).is_valid_hex_number():
  625. var bitea := PoolByteArray()
  626. for i_hex in value_data.size() * 0.5 - 1:
  627. bitea.append(("0x" + bitea_data[i_hex + 2] + bitea_data[i_hex + 2]).hex_to_int())
  628. # The result.
  629. row.append(bitea)
  630. else:
  631. push_error("[PostgreSQLClient:%d] The backend sent an invalid BITEA object." % [get_instance_id()])
  632. close(false)
  633. response_buffer = PoolByteArray()
  634. return
  635. "timestamp":
  636. ### TIMESTAMP ###
  637. pass
  638. "date":
  639. ### DATE ###
  640. pass
  641. "interval":
  642. ### INTERVAL ###
  643. pass
  644. DataTypePostgreSQL.UUID:
  645. ### UUID ###
  646. # The type returned is String.
  647. # Ideally we should validate the value sent by the backend...
  648. # The result.
  649. row.append(value_data.get_string_from_ascii())
  650. DataTypePostgreSQL.CIDR:
  651. ### CIDR ###
  652. # The type returned is String.
  653. # Ideally we should validate the value sent by the backend with the line if below...
  654. #value_data.get_string_from_ascii().is_valid_ip_address()
  655. # The result.
  656. row.append(value_data.get_string_from_ascii())
  657. DataTypePostgreSQL.INET:
  658. ### INET ###
  659. # The type returned is String.
  660. # Ideally we should validate the value sent by the backend with the line if below...
  661. #value_data.get_string_from_ascii().is_valid_ip_address()
  662. # The result.
  663. row.append(value_data.get_string_from_ascii())
  664. DataTypePostgreSQL.MACADDR:
  665. ### MACADDR ###
  666. # The type returned is String.
  667. # Ideally we should validate the value sent by the backend...
  668. # The result.
  669. row.append(value_data.get_string_from_ascii())
  670. DataTypePostgreSQL.MACADDR8:
  671. ### MACADDR8 ###
  672. # The type returned is String.
  673. # Ideally we should validate the value sent by the backend...
  674. # The result.
  675. row.append(value_data.get_string_from_ascii())
  676. DataTypePostgreSQL.POINT:
  677. ### POINT ###
  678. # The type returned is Vector2.
  679. var regex = RegEx.new()
  680. error = regex.compile("^\\((-?\\d+(?:\\.\\d+)?),(-?\\d+(?:\\.\\d+)?)\\)")
  681. if error:
  682. push_error("[PostgreSQLClient:%d] RegEx compilation of POINT object failed. (Error: %d)" % [get_instance_id(), error])
  683. close(false)
  684. response_buffer = PoolByteArray()
  685. return
  686. var result = regex.search(value_data.get_string_from_ascii())
  687. if result:
  688. # The result.
  689. row.append(Vector2(float(result.strings[1]), float(result.strings[2])))
  690. else:
  691. push_error("[PostgreSQLClient:%d] The backend sent an invalid POINT object." % [get_instance_id()])
  692. close(false)
  693. response_buffer = PoolByteArray()
  694. return
  695. DataTypePostgreSQL.BOX:
  696. ### BOX ###
  697. # The type returned is Rect2.
  698. var regex = RegEx.new()
  699. error = regex.compile("^\\((-?\\d+(?:\\.\\d+)?),(-?\\d+(?:\\.\\d+)?)\\),\\((-?\\d+(?:\\.\\d+)?),(-?\\d+(?:\\.\\d+)?)\\)")
  700. if error:
  701. push_error("[PostgreSQLClient:%d] RegEx compilation of BOX object failed. (Error: %d)" % [get_instance_id(), error])
  702. close(false)
  703. response_buffer = PoolByteArray()
  704. return
  705. var result = regex.search(value_data.get_string_from_ascii())
  706. if result:
  707. # The result.
  708. row.append(Rect2(float(result.strings[3]), float(result.strings[4]), float(result.strings[1]), float(result.strings[2])))
  709. else:
  710. push_error("[PostgreSQLClient:%d] The backend sent an invalid BOX object." % [get_instance_id()])
  711. close(false)
  712. response_buffer = PoolByteArray()
  713. return
  714. DataTypePostgreSQL.LSEG:
  715. ### LSEG ###
  716. # The type returned is PoolVector2Array.
  717. var regex = RegEx.new()
  718. error = regex.compile("^\\[\\((-?\\d+(?:\\.\\d+)?),(-?\\d+(?:\\.\\d+)?)\\),\\((-?\\d+(?:\\.\\d+)?),(-?\\d+(?:\\.\\d+)?)\\)\\]")
  719. if error:
  720. push_error("[PostgreSQLClient:%d] RegEx compilation of LSEG object failed. (Error: %d)" % [get_instance_id(), error])
  721. close(false)
  722. response_buffer = PoolByteArray()
  723. return
  724. var result = regex.search(value_data.get_string_from_ascii())
  725. if result:
  726. # The result.
  727. row.append(PoolVector2Array([
  728. Vector2(float(result.strings[1]), float(result.strings[2])),
  729. Vector2(float(result.strings[3]), float(result.strings[4]))
  730. ]))
  731. else:
  732. push_error("[PostgreSQLClient:%d] The backend sent an invalid LSEG object." % [get_instance_id()])
  733. close(false)
  734. response_buffer = PoolByteArray()
  735. return
  736. "polygon":
  737. ### POLYGON ###
  738. # The type returned is PoolVector2Array.
  739. row.append(PoolVector2Array())
  740. "path":
  741. ### PATH ###
  742. # The type returned is PoolVector2Array.
  743. row.append(PoolVector2Array())
  744. DataTypePostgreSQL.LINE:
  745. ### LINE ###
  746. # The type returned is Vector3.
  747. var regex = RegEx.new()
  748. error = regex.compile("^\\{(-?\\d+(?:\\.\\d+)?),(-?\\d+(?:\\.\\d+)?),(-?\\d+(?:\\.\\d+)?)\\}")
  749. if error:
  750. push_error("[PostgreSQLClient:%d] RegEx compilation of LINE object failed. (Error: %d)" % [get_instance_id(), error])
  751. close(false)
  752. response_buffer = PoolByteArray()
  753. return
  754. var result = regex.search(value_data.get_string_from_ascii())
  755. if result:
  756. # The result.
  757. row.append(Vector3(float(result.strings[1]), float(result.strings[2]), float(result.strings[3])))
  758. else:
  759. push_error("[PostgreSQLClient:%d] The backend sent an invalid LINE object." % [get_instance_id()])
  760. close(false)
  761. response_buffer = PoolByteArray()
  762. return
  763. DataTypePostgreSQL.CIRCLE:
  764. ### CIRCLE ###
  765. # The type returned is Vector3.
  766. var regex = RegEx.new()
  767. error = regex.compile("^<\\((-?\\d+(?:\\.\\d+)?),(-?\\d+(?:\\.\\d+)?)\\),(\\d+(\\.\\d+)?)>")
  768. if error:
  769. push_error("[PostgreSQLClient:%d] RegEx compilation of CIRCLE object failed. (Error: %d)" % [get_instance_id(), error])
  770. close(false)
  771. response_buffer = PoolByteArray()
  772. return
  773. var result = regex.search(value_data.get_string_from_ascii())
  774. if result:
  775. # The result.
  776. row.append(Vector3(float(result.strings[1]), float(result.strings[2]), float(result.strings[3])))
  777. else:
  778. push_error("[PostgreSQLClient:%d] The backend sent an invalid CIRCLE object." % [get_instance_id()])
  779. close(false)
  780. response_buffer = PoolByteArray()
  781. return
  782. DataTypePostgreSQL.DATE:
  783. ### DATE ###
  784. # The type returned is Date.
  785. # Ideally we should validate the value sent by the backend...
  786. # The result.
  787. row.append(value_data.get_string_from_ascii())
  788. DataTypePostgreSQL.TIME:
  789. ### TIME ###
  790. # The type returned is Time.
  791. # Ideally we should validate the value sent by the backend...
  792. # The result.
  793. row.append(value_data.get_string_from_ascii())
  794. _:
  795. # The type returned is PoolByteArray.
  796. row.append(value_data)
  797. cursor += value_length + 4
  798. # The result.
  799. postgresql_query_result_instance.data_row.append(row)
  800. postgresql_query_result_instance.raw_data_row.append(raw_row)
  801. 'E':
  802. ### ErrorResponse ###
  803. # Identifies the message as an error.
  804. # The message body consists of one or more identified fields, followed by a zero byte as a terminator.
  805. # Fields can appear in any order. For each field there is the following:
  806. for champ_data in split_pool_byte_array(response_buffer.subarray(5, message_length - 1), 0):
  807. var champ: String = champ_data.get_string_from_ascii()
  808. # A code identifying the field type; if zero, this is the message terminator and no string follows.
  809. var field_type_code := champ[0]
  810. # The field value.
  811. var value := champ.trim_prefix(field_type_code)
  812. match field_type_code:
  813. 'S':
  814. if value == "FATAL":
  815. # For security reasons, the dictionary is empty when the frontend is not connected to the backend.
  816. parameter_status = {}
  817. # For security reasons, the dictionary is empty when the frontend is not connected to the backend.
  818. error_object = {}
  819. status = Status.STATUS_DISCONNECTED
  820. status_ssl = 0
  821. emit_signal("connection_closed", true)
  822. error_object["severity"] = value
  823. 'V':
  824. error_object["severity_no_localized"] = value
  825. 'C':
  826. error_object["SQLSTATE_code"] = value
  827. 'M':
  828. error_object["message"] = value
  829. push_error("[PostgreSQLClient:%d] %s" % [get_instance_id(), value])
  830. 'D':
  831. error_object["detail"] = value
  832. 'H':
  833. error_object["hint"] = value
  834. 'P':
  835. error_object["position"] = value
  836. 'p':
  837. error_object["internal_position"] = value
  838. 'q':
  839. error_object["internal_query"] = value
  840. 'W':
  841. error_object["where"] = value
  842. 's':
  843. error_object["schema_name"] = value
  844. 't':
  845. error_object["table_name"] = value
  846. 'c':
  847. error_object["column_name"] = value
  848. 'd':
  849. error_object["constraint_name"] = value
  850. 'n':
  851. error_object["constraint_name"] = value
  852. 'F':
  853. error_object["file"] = value
  854. 'L':
  855. error_object["line"] = value
  856. 'R':
  857. error_object["routine"] = value
  858. _:
  859. # Since more field types might be added in future, frontends should silently ignore fields of unrecognized type.
  860. pass
  861. if error_object["severity"] == "FATAL":
  862. status = Status.STATUS_ERROR
  863. if status != Status.STATUS_CONNECTED:
  864. emit_signal("authentication_error", error_object.duplicate())
  865. 'G':
  866. ### CopyInResponse ###
  867. # The message "CopyInResponse" identifies the message as a Start Copy In response. The frontend must now send copy-in data (if not prepared to do so, send a CopyFail message).
  868. buffer = StreamPeerBuffer.new()
  869. # Get overall copy format code.
  870. # 0 indicates the overall COPY format is textual (rows separated by newlines, columns separated by separator characters, etc). 1 indicates the overall copy format is binary (similar to DataRow format). See COPY for more information.
  871. var overall_copy_format_code = response_buffer.subarray(5, 6)
  872. overall_copy_format_code.invert()
  873. _unused = buffer.put_data(overall_copy_format_code)
  874. buffer.seek(0)
  875. overall_copy_format_code = buffer.get_u8()
  876. # Get the number of columns in the data to be copied.
  877. var number_of_columns = response_buffer.subarray(7, 9)
  878. number_of_columns.invert()
  879. _unused = buffer.put_data(number_of_columns)
  880. buffer.seek(1)
  881. number_of_columns = buffer.get_u16()
  882. # Get the format codes to be used for each column.
  883. # Each must presently be zero (text) or one (binary). All must be zero if the overall copy format is textual.
  884. for index in number_of_columns:
  885. var format_code = response_buffer.subarray(10, 12)
  886. format_code.invert()
  887. _unused = buffer.put_data(format_code)
  888. buffer.seek(2 * index + 3)
  889. format_code = buffer.get_u16()
  890. # The result.
  891. print(format_code)
  892. push_warning("[PostgreSQLClient:%d] CopyInResponse, no support." % [get_instance_id()])
  893. 'H':
  894. ### CopyOutResponse ###
  895. # The message "CopyOutResponse" identifies the message as a Start Copy Out response. This message will be followed by copy-out data.
  896. buffer = StreamPeerBuffer.new()
  897. # Get overall copy format code.
  898. # 0 indicates the overall COPY format is textual (rows separated by newlines, columns separated by separator characters, etc). 1 indicates the overall copy format is binary (similar to DataRow format). See COPY for more information.
  899. var overall_copy_format_code = response_buffer.subarray(5, 6)
  900. overall_copy_format_code.invert()
  901. _unused = buffer.put_data(overall_copy_format_code)
  902. buffer.seek(0)
  903. overall_copy_format_code = buffer.get_8()
  904. # Get the number of columns in the data to be copied.
  905. var number_of_columns = response_buffer.subarray(7, 9)
  906. number_of_columns.invert()
  907. _unused = buffer.put_data(number_of_columns)
  908. buffer.seek(1)
  909. number_of_columns = buffer.get_16()
  910. # Get the format codes to be used for each column.
  911. # Each must presently be zero (text) or one (binary). All must be zero if the overall copy format is textual.
  912. for index in number_of_columns:
  913. var format_code = response_buffer.subarray(10, 12)
  914. format_code.invert()
  915. _unused = buffer.put_data(format_code)
  916. buffer.seek(2 * index + 3)
  917. format_code = buffer.get_16()
  918. # The result.
  919. print(format_code)
  920. push_warning("[PostgreSQLClient:%d] CopyOutResponse, no support." % [get_instance_id()])
  921. 'N':
  922. ### NoticeResponse ###
  923. # Identifies the message as a notice.
  924. var notice_object := {}
  925. # The message body consists of one or more identified fields, followed by a zero byte as a terminator.
  926. # Fields can appear in any order.
  927. # For each field there is the following:
  928. for champ_data in split_pool_byte_array(response_buffer.subarray(5, message_length - 1), 0):
  929. var champ: String = champ_data.get_string_from_ascii()
  930. # A code identifying the field type; if zero, this is the message terminator and no string follows.
  931. var field_type_code := champ[0]
  932. # The field value.
  933. var value := champ.trim_prefix(field_type_code)
  934. match field_type_code:
  935. 'S':
  936. notice_object["severity"] = value
  937. 'V':
  938. notice_object["severity_no_localized"] = value
  939. 'C':
  940. notice_object["SQLSTATE_code"] = value
  941. 'M':
  942. notice_object["message"] = value
  943. 'D':
  944. notice_object["detail"] = value
  945. 'H':
  946. notice_object["hint"] = value
  947. 'P':
  948. notice_object["position"] = value
  949. 'p':
  950. notice_object["internal_position"] = value
  951. 'q':
  952. notice_object["internal_query"] = value
  953. 'W':
  954. notice_object["where"] = value
  955. 's':
  956. notice_object["schema_name"] = value
  957. 't':
  958. notice_object["table_name"] = value
  959. 'c':
  960. notice_object["column_name"] = value
  961. 'd':
  962. notice_object["constraint_name"] = value
  963. 'n':
  964. notice_object["constraint_name"] = value
  965. 'F':
  966. notice_object["file"] = value
  967. 'L':
  968. notice_object["line"] = value
  969. 'R':
  970. notice_object["routine"] = value
  971. _:
  972. # Since more field types might be added in future, frontends should silently ignore fields of unrecognized type.
  973. pass
  974. var last_datas_command_sql = datas_command_sql.back()
  975. if last_datas_command_sql:
  976. last_datas_command_sql.notice = notice_object
  977. 'I':
  978. ### EmptyQueryResponse ###
  979. # Identifies the message as a response to an empty query string. (This substitutes for CommandComplete.)
  980. pass
  981. 'K':
  982. ### BackendKeyData ####
  983. # Identifies the message as cancellation key data. The frontend must save these values if it wishes to be able to issue CancelRequest messages later.
  984. # Get the process ID of this backend.
  985. var process_backend_id_buffer = response_buffer.subarray(5, 8)
  986. process_backend_id_buffer.invert()
  987. _unused = buffer.put_data(process_backend_id_buffer)
  988. buffer.seek(4)
  989. # The result.
  990. process_backend_id = buffer.get_u32()
  991. # Get the secret key of this backend.
  992. var process_backend_secret_key_buffer = response_buffer.subarray(9, message_length)
  993. process_backend_secret_key_buffer.invert()
  994. _unused = buffer.put_data(process_backend_secret_key_buffer)
  995. buffer.seek(8)
  996. # The result.
  997. process_backend_secret_key = buffer.get_u32()
  998. 'R':
  999. ### Authentication ###
  1000. # Identifies the message as an authentication request.
  1001. var authentication_type_data := response_buffer.subarray(5, 8)
  1002. authentication_type_data.invert()
  1003. _unused = buffer.put_data(authentication_type_data)
  1004. buffer.seek(4)
  1005. var authentication_type := buffer.get_32()
  1006. match authentication_type:
  1007. 0:
  1008. ### AuthenticationOk ###
  1009. # Specifies that the authentication was successful.
  1010. status = Status.STATUS_CONNECTING
  1011. 2:
  1012. ### AuthenticationKerberosV5 ###
  1013. # Specifies that Kerberos V5 authentication is required.
  1014. # rfc4120
  1015. # No support
  1016. push_error("AuthenticationKerberosV5 No support")
  1017. close(false)
  1018. response_buffer = PoolByteArray()
  1019. return
  1020. 3:
  1021. ### AuthenticationCleartextPassword ###
  1022. # Specifies that a clear-text password is required.
  1023. response_buffer = PoolByteArray()
  1024. return request('p', password_global.to_utf8())
  1025. 5:
  1026. ### AuthentificationMD5Password ###
  1027. # Specifies that an MD5-encrypted password is required.
  1028. var hashing_context = HashingContext.new()
  1029. hashing_context.start(HashingContext.HASH_MD5)
  1030. hashing_context.update((password_global + user_global).md5_buffer().hex_encode().to_ascii() + response_buffer.subarray(9, 12))
  1031. response_buffer = PoolByteArray()
  1032. return request('p', ("md5" + hashing_context.finish().hex_encode()).to_ascii() + PoolByteArray([0]))
  1033. 6:
  1034. ### AuthenticationSCMCredential ###
  1035. # Specifies that an SCM credentials message is required.
  1036. # No support
  1037. push_error("AuthenticationSCMCredential No support")
  1038. close(false)
  1039. response_buffer = PoolByteArray()
  1040. return
  1041. 7:
  1042. ### AuthenticationGSS ###
  1043. # Specifies that GSSAPI authentication is required.
  1044. # No support
  1045. push_error("AuthenticationGSS No support")
  1046. close(false)
  1047. response_buffer = PoolByteArray()
  1048. return
  1049. 8:
  1050. ### AuthenticationGSSContinue ###
  1051. # Specifies that this message contains GSSAPI or SSPI data.
  1052. # No support
  1053. push_error("AuthenticationGSSContinue No support")
  1054. close(false)
  1055. response_buffer = PoolByteArray()
  1056. return
  1057. 9:
  1058. ### AuthenticationSSPI ###
  1059. # Specifies that SSPI authentication is required.
  1060. # No support
  1061. push_error("AuthenticationSSPI No support")
  1062. close(false)
  1063. response_buffer = PoolByteArray()
  1064. return
  1065. 10:
  1066. ### AuthenticationSASL ###
  1067. # Specifies that SASL authentication is required.
  1068. # Get the message body is a list of SASL authentication mechanisms, in the server's order of preference. A zero byte is required as terminator after the last authentication mechanism name.
  1069. # For each mechanism, there is the following:
  1070. for name_sasl_authentication_mechanism in split_pool_byte_array(response_buffer.subarray(9, message_length - 1), 0):
  1071. match name_sasl_authentication_mechanism.get_string_from_ascii():
  1072. "SCRAM-SHA-256":
  1073. ### SASLInitialResponse ###
  1074. # Identifies the message as an initial SASL response. Note that this is also used for GSSAPI, SSPI and password response messages. The exact message type is deduced from the context.
  1075. var crypto := Crypto.new()
  1076. var nonce = Marshalls.raw_to_base64(crypto.generate_random_bytes(24))
  1077. client_first_message = "%c,,n=%s,r=%s" % ['n', "", nonce] # When SCRAM-SHA-256 is used in PostgreSQL, the server will ignore the user name that the client sends in the client-first-message. The user name that was already sent in the startup message is used instead.
  1078. var len_client_first_message := get_32byte_invert(len(client_first_message), true)
  1079. var sasl_initial_response := request('p', "SCRAM-SHA-256".to_ascii() + PoolByteArray([0]) + len_client_first_message + client_first_message.to_utf8())
  1080. if stream_peer_ssl.get_status() == stream_peer_ssl.STATUS_CONNECTED:
  1081. stream_peer_ssl.put_data(sasl_initial_response)
  1082. else:
  1083. _unused = peer.put_data(sasl_initial_response)
  1084. response_buffer = PoolByteArray()
  1085. return
  1086. "SCRAM-SHA-256-PLUS":
  1087. continue # I'm still not done implementing SCRAM-SHA-256-PLUS, so we'll skip it for now.
  1088. # /!\ Not end /!\
  1089. ### SASLInitialResponse ###
  1090. # Identifies the message as an initial SASL response. Note that this is also used for GSSAPI, SSPI and password response messages. The exact message type is deduced from the context.
  1091. var crypto := Crypto.new()
  1092. var nonce = Marshalls.raw_to_base64(crypto.generate_random_bytes(24))
  1093. client_first_message = "%c,,n=%s,r=%s" % ['y', "", nonce] # When SCRAM-SHA-256-PLUS is used in PostgreSQL, the server will ignore the user name that the client sends in the client-first-message. The user name that was already sent in the startup message is used instead.
  1094. var len_client_first_message := get_32byte_invert(len(client_first_message), true)
  1095. var sasl_initial_response := request('p', "SCRAM-SHA-256-PLUS".to_ascii() + PoolByteArray([0]) + len_client_first_message + client_first_message.to_utf8())
  1096. if stream_peer_ssl.get_status() == stream_peer_ssl.STATUS_CONNECTED:
  1097. stream_peer_ssl.put_data(sasl_initial_response)
  1098. else:
  1099. _unused = peer.put_data(sasl_initial_response)
  1100. response_buffer = PoolByteArray()
  1101. return
  1102. "SCRAM-SHA-1":
  1103. # No implemented.
  1104. pass
  1105. "SCRAM-SHA-1-PLUS":
  1106. # No implemented.
  1107. pass
  1108. "CRAM-MD5":
  1109. # No implemented.
  1110. pass
  1111. "CRAM-MD5-PLUS":
  1112. # No implemented.
  1113. pass
  1114. push_error("[PostgreSQLClient:%d] No SASL mechanism offered by the backend is supported by the frontend for SASL authentication." % [get_instance_id()])
  1115. close(false)
  1116. response_buffer = PoolByteArray()
  1117. return
  1118. 11:
  1119. ### AuthenticationSASLContinue ###
  1120. # Specifies that this message contains a SASL challenge.
  1121. # SCRAM-SHA-256
  1122. var server_first_message = response_buffer.subarray(9, message_length).get_string_from_ascii()
  1123. var server_nonce = server_first_message.split(',')[0].substr(2)
  1124. var server_salt = Marshalls.base64_to_raw(server_first_message.split(',')[1].substr(2))
  1125. var server_iterations := int(server_first_message.split(',')[2].substr(2))
  1126. var client_final_message := "c=biws,r=%s" % [server_nonce]
  1127. # On devrait passer le mot de passe (password_global) dans la fonction SASLprep (rfc7613) (or SASLprep, rfc4013) non implémenté si desous...
  1128. salted_password = pbkdf2(HashingContext.HASH_SHA256, password_global.to_utf8(), server_salt, server_iterations)
  1129. var crypto = Crypto.new()
  1130. var client_key = crypto.hmac_digest(HashingContext.HASH_SHA256, salted_password, "Client Key".to_ascii())
  1131. var hashing_context = HashingContext.new()
  1132. hashing_context.start(HashingContext.HASH_SHA256)
  1133. hashing_context.update(client_key)
  1134. var stored_key = hashing_context.finish()
  1135. # AuthMessage is just a concatenation of the initial client message, server challenge, and client response (without ClientProof).
  1136. var client_first_message_bare = client_first_message.substr(3)
  1137. client_first_message = ""
  1138. auth_message = client_first_message_bare + ',' + server_first_message + ',' + client_final_message
  1139. var client_signature = crypto.hmac_digest(HashingContext.HASH_SHA256, stored_key, auth_message.to_utf8())
  1140. var client_proof_buffer := PoolByteArray()
  1141. for index in client_key.size():
  1142. client_proof_buffer.append(client_key[index] ^ client_signature[index])
  1143. var client_proof := Marshalls.raw_to_base64(client_proof_buffer)
  1144. client_final_message += ",p=" + client_proof
  1145. var authentication_sasl_continue := request('p', client_final_message.to_ascii())
  1146. if stream_peer_ssl.get_status() == stream_peer_ssl.STATUS_CONNECTED:
  1147. stream_peer_ssl.put_data(authentication_sasl_continue)
  1148. else:
  1149. _unused = peer.put_data(authentication_sasl_continue)
  1150. 12:
  1151. ### AuthenticationSASLFinal ###
  1152. # Specifies that SASL authentication has completed.
  1153. var server_final_message = response_buffer.subarray(9, message_length).get_string_from_ascii()
  1154. # The client verifies the proof from the server by calculating the ServerKey and the ServerSignature, then comparing its ServerSignature to that received from the server.
  1155. # If they are the same, the client has proof that the server has access to the ServerKey.
  1156. var crypto = Crypto.new()
  1157. var server_key = crypto.hmac_digest(HashingContext.HASH_SHA256, salted_password, "Server Key".to_ascii())
  1158. salted_password = PoolByteArray()
  1159. var server_signature = crypto.hmac_digest(HashingContext.HASH_SHA256, server_key, auth_message.to_utf8())
  1160. auth_message = ""
  1161. var server_proof := PoolByteArray()
  1162. for index in server_key.size():
  1163. server_proof.append(server_key[index] ^ server_signature[index])
  1164. # Get server proof response
  1165. var server_proof_response = server_final_message.substr(2)
  1166. if server_proof_response != Marshalls.raw_to_base64(server_signature):
  1167. # /!\ We should normally trigger the "authentication_error" signal but it is still not implemented... /!\
  1168. push_error("[PostgreSQLClient:%d] An error occurred during SASL authentication. The SCRAM dialogue between the frontend and the backend does not end as expected. The server could not prove that it was in possession of ServerKey. The backend does not seem reliable for the frontend. The authentication attempt failed. Connection between frontend and backend interrupted." % [get_instance_id()])
  1169. close(false)
  1170. response_buffer = PoolByteArray()
  1171. return
  1172. _:
  1173. push_error("[PostgreSQLClient:%d] The backend requires the frontend to use an authentication method that it does not support. Unknown authentication code." % [get_instance_id()])
  1174. close(false)
  1175. response_buffer = PoolByteArray()
  1176. return
  1177. 'S':
  1178. ### ParameterStatus ###
  1179. # Identifies the message as a run-time parameter status report.
  1180. var situation_report_data := split_pool_byte_array(response_buffer.subarray(5, message_length), 0)
  1181. # Get the name of the run-time parameter being reported.
  1182. var parameter: String = situation_report_data[0].get_string_from_utf8()
  1183. # Get the current value of the parameter.
  1184. var value: String = situation_report_data[1].get_string_from_utf8()
  1185. # The result
  1186. parameter_status[parameter] = value
  1187. 'T':
  1188. ### RowDescription ###
  1189. # Get the number of fields in a row (can be zero).
  1190. var number_of_fields_in_a_row := response_buffer.subarray(5, 6)
  1191. number_of_fields_in_a_row.invert()
  1192. _unused = buffer.put_data(number_of_fields_in_a_row)
  1193. buffer.seek(4)
  1194. postgresql_query_result_instance.number_of_fields_in_a_row = buffer.get_u16()
  1195. # Then, for each field...
  1196. var cursor := 7
  1197. for _index in postgresql_query_result_instance.number_of_fields_in_a_row:
  1198. # Get the field name.
  1199. var field_name := ""
  1200. for octet in response_buffer.subarray(cursor, message_length):
  1201. field_name += char(octet)
  1202. # If we get to the end of the chain, we get out of the loop.
  1203. if not octet:
  1204. break
  1205. cursor += len(field_name)
  1206. buffer = StreamPeerBuffer.new()
  1207. # Get the object ID of the table.
  1208. # If the field can be identified as a column of a specific table, the object ID of the table; otherwise zero.
  1209. var table_object_id = response_buffer.subarray(cursor, cursor + 4)
  1210. table_object_id.invert()
  1211. _unused = buffer.put_data(table_object_id)
  1212. buffer.seek(0)
  1213. table_object_id = buffer.get_u32()
  1214. # Get the attribute number of the column.
  1215. # If the field can be identified as a column of a specific table, the attribute number of the column; otherwise zero.
  1216. var column_index = response_buffer.subarray(cursor + 5, cursor + 6)
  1217. column_index.invert()
  1218. _unused = buffer.put_data(column_index)
  1219. buffer.seek(4)
  1220. column_index = buffer.get_u16()
  1221. # Get the object ID of the field's data type.
  1222. var type_object_id = response_buffer.subarray(cursor + 7, cursor + 10)
  1223. type_object_id.invert()
  1224. _unused = buffer.put_data(type_object_id)
  1225. buffer.seek(6)
  1226. type_object_id = buffer.get_u32()
  1227. # Get the data type size (see pg_type.typlen).
  1228. # Note that negative values denote variable-width types.
  1229. var data_type_size = response_buffer.subarray(cursor + 11, cursor + 12)
  1230. data_type_size.invert()
  1231. _unused = buffer.put_data(data_type_size)
  1232. buffer.seek(10)
  1233. data_type_size = buffer.get_u16()
  1234. # Get the type modifier (see pg_attribute.atttypmod).
  1235. # The meaning of the modifier is type-specific.
  1236. var type_modifier = response_buffer.subarray(cursor + 13, cursor + 16)
  1237. type_modifier.invert()
  1238. _unused = buffer.put_data(type_modifier)
  1239. buffer.seek(12)
  1240. type_modifier = buffer.get_u32()
  1241. # Get the format code being used for the field.
  1242. # Currently will be zero (text) or one (binary).
  1243. # In a RowDescription returned from the statement variant of Describe, the format code is not yet known and will always be zero.
  1244. var format_code = response_buffer.subarray(cursor + 17, cursor + 18)
  1245. format_code.invert()
  1246. _unused = buffer.put_data(format_code)
  1247. buffer.seek(16)
  1248. format_code = buffer.get_u16()
  1249. cursor += 19
  1250. # The result.
  1251. postgresql_query_result_instance.row_description.append({
  1252. "field_name": field_name,
  1253. "table_object_id": table_object_id,
  1254. "column_index": column_index,
  1255. "type_object_id": type_object_id,
  1256. "data_type_size": data_type_size,
  1257. "type_modifier": type_modifier,
  1258. "format_code": format_code
  1259. })
  1260. 'V':
  1261. ### FunctionCallResponse ###
  1262. # Identifies the message as a function call result.
  1263. push_error("FunctionCallResponse no implemented.")
  1264. 'W':
  1265. ### CopyBothResponse ###
  1266. # The message "CopyBothResponse" identifies the message as a Start Copy Both response. This message is used only for Streaming Replication.
  1267. buffer = StreamPeerBuffer.new()
  1268. # Get overall copy format code.
  1269. # 0 indicates the overall COPY format is textual (rows separated by newlines, columns separated by separator characters, etc). 1 indicates the overall copy format is binary (similar to DataRow format). See COPY for more information.
  1270. var overall_copy_format_code = response_buffer.subarray(5, 6)
  1271. overall_copy_format_code.invert()
  1272. _unused = buffer.put_data(overall_copy_format_code)
  1273. buffer.seek(0)
  1274. overall_copy_format_code = buffer.get_8()
  1275. # Get the number of columns in the data to be copied.
  1276. var number_of_columns = response_buffer.subarray(7, 9)
  1277. number_of_columns.invert()
  1278. _unused = buffer.put_data(number_of_columns)
  1279. buffer.seek(1)
  1280. number_of_columns = buffer.get_16()
  1281. # Get the format codes to be used for each column.
  1282. # Each must presently be zero (text) or one (binary). All must be zero if the overall copy format is textual.
  1283. for index in number_of_columns:
  1284. var format_code = response_buffer.subarray(10, 12)
  1285. format_code.invert()
  1286. _unused = buffer.put_data(format_code)
  1287. buffer.seek(2 * index + 3)
  1288. format_code = buffer.get_16()
  1289. # The result.
  1290. print(format_code)
  1291. push_warning("[PostgreSQLClient:%d] CopyBothResponse, no support." % [get_instance_id()])
  1292. 'Z':
  1293. ### ReadyForQuery ###
  1294. # Identifies the message type. ReadyForQuery is sent whenever the backend is ready for a new query cycle.
  1295. # Get current backend transaction status indicator.
  1296. match char(response_buffer[message_length]):
  1297. 'I':
  1298. # If idle (if not in a transaction block).
  1299. prints("Not in a transaction block.")
  1300. 'T':
  1301. # If in a transaction block.
  1302. prints("In a transaction block.")
  1303. 'E':
  1304. # If in a failed transaction block (queries will be rejected until block is ended).
  1305. prints("In a failed transaction block.")
  1306. _:
  1307. # We close the connection with the backend if current backend transaction status indicator is not recognized.
  1308. close(false)
  1309. var data_returned := datas_command_sql
  1310. datas_command_sql = []
  1311. response_buffer = PoolByteArray()
  1312. if status == Status.STATUS_CONNECTING:
  1313. status = Status.STATUS_CONNECTED
  1314. # Once logged in, the database password and username are deleted from memory for security reasons.
  1315. password_global = ""
  1316. user_global = ""
  1317. emit_signal("connection_established")
  1318. return data_returned
  1319. 'c':
  1320. ### CopyDone ###
  1321. # Identifies the message as a COPY-complete indicator.
  1322. print("CopyDone")
  1323. 'd':
  1324. ### CopyData ###
  1325. # Identifies the message as COPY data.
  1326. # Get data that forms part of a COPY data stream. Messages sent from the backend will always correspond to single data rows.
  1327. var data := response_buffer.subarray(5, message_length)
  1328. # The result
  1329. print(data)
  1330. 'n':
  1331. ### NoData ###
  1332. # Identifies the message as a no-data indicator.
  1333. pass
  1334. 's':
  1335. ### ReadyForQuery ###
  1336. #Identifies the message as a portal-suspended indicator. Note this only appears if an Execute message's row-count limit was reached.
  1337. pass
  1338. 't':
  1339. ### ParameterDescription ###
  1340. # Identifies the message as a parameter description.
  1341. # Get the number of parameters used by the statement (can be zero).
  1342. var number_of_parameters = response_buffer.subarray(5, 6)
  1343. number_of_parameters.invert()
  1344. _unused = buffer.put_data(number_of_parameters)
  1345. buffer.seek(4)
  1346. number_of_parameters = buffer.get_16()
  1347. # Then, for each parameter, there is the following:
  1348. var data_types = []
  1349. var cursor := 7
  1350. for index in number_of_parameters:
  1351. # Get the object ID of the parameter data type.
  1352. var object_id = response_buffer.subarray(cursor, cursor + 4)
  1353. object_id.invert()
  1354. _unused = buffer.put_data(object_id)
  1355. buffer.seek(cursor + index - 1)
  1356. data_types.append(buffer.get_32())
  1357. cursor += 5
  1358. # The result.
  1359. print(data_types)
  1360. 'v':
  1361. ### NegotiateProtocolVersion ###
  1362. # Identifies the message as a protocol version negotiation message.
  1363. # Get newest minor protocol version supported by the server for the major protocol version requested by the client.
  1364. var minor_protocol_version = response_buffer.subarray(5, 8)
  1365. minor_protocol_version.invert()
  1366. _unused = buffer.put_data(minor_protocol_version)
  1367. buffer.seek(4)
  1368. minor_protocol_version = buffer.get_u32()
  1369. # Get the number of protocol options not recognized by the server.
  1370. var number_of_options = response_buffer.subarray(9, 13)
  1371. number_of_options.invert()
  1372. _unused = buffer.put_data(number_of_options)
  1373. buffer.seek(8)
  1374. number_of_options = buffer.get_u32()
  1375. # Then, for each protocol option not recognized by the server...
  1376. var _cursor := 0
  1377. for _index in number_of_options:
  1378. # Get the option name.
  1379. pass
  1380. # The result.
  1381. prints(minor_protocol_version)
  1382. '1':
  1383. ### ParseComplete ###
  1384. # Identifies the message as a Parse-complete indicator.
  1385. pass
  1386. '2':
  1387. ### BindComplete ###
  1388. # Identifies the message as a Bind-complete indicator.
  1389. pass
  1390. '3':
  1391. ### CloseComplete ###
  1392. # Identifies the message as a Close-complete indicator.
  1393. pass
  1394. var message_type:
  1395. # We close the connection with the backend if the type of message is not recognized.
  1396. status = Status.STATUS_ERROR
  1397. push_error("[PostgreSQLClient:%d] The type of message sent by the backend is not recognized (%c)." % [get_instance_id(), message_type])
  1398. close(false)
  1399. # The response from the server can contain several messages, we read the message then delete the message to be processed to read the next one in the loop.
  1400. if response_buffer.size() != message_length + 1:
  1401. response_buffer = response_buffer.subarray(message_length + 1, -1)
  1402. else:
  1403. response_buffer.resize(0)
  1404. if client.get_status() != StreamPeerTCP.STATUS_CONNECTED:
  1405. break