root/HTTP/DB.lua

Revision 1563 (checked in by rsz, 4 months ago)

cleanup

Line 
1 --------------------------------------------------------------------------------
2 -- Title:               DB.lua
3 -- Description:         Like a square peg in a round hole
4 -- Author:              Raphaël Szwarc http://alt.textdrive.com/lua/
5 -- Creation Date:       January 30, 2007
6 -- Legal:               Copyright (C) 2007 Raphaël Szwarc
7 --                      Under the terms of the MIT License
8 --                      http://www.opensource.org/licenses/mit-license.html
9 --------------------------------------------------------------------------------
10
11 -- import dependencies
12 local assert = assert
13 local getmetatable = getmetatable
14 local ipairs = ipairs
15 local module = module
16 local require = require
17 local select = select
18 local setmetatable = setmetatable
19 local tostring = tostring
20 local type = type
21 local unpack = unpack
22
23 --------------------------------------------------------------------------------
24 -- DB
25 --------------------------------------------------------------------------------
26
27 module( 'DB' )
28 _VERSION = '1.0'
29
30 local self = setmetatable( _M, {} )
31 local meta = getmetatable( self )
32
33 local function NewEnvironment( aURL, ... )
34     local aType = assert( aURL.scheme, 'missing scheme in ' .. aURL )
35     local aModule = require( 'luasql.' .. aType )
36     local LuaSQL = require( 'luasql' )
37     local aFunction = assert( LuaSQL[ aType ], 'missing ' .. aType .. ' initialization function in luasql'  )
38     local anEnvironment = assert( aFunction( ... ) )
39    
40     return anEnvironment
41 end
42
43 local function Source( aURL )
44     local URLPath = require( 'URLPath' )
45     local aPath = URLPath( aURL.path )
46
47     aPath.absolute = false
48
49     return URLPath.Decode( tostring( aPath ) )
50 end
51
52 local function NewConnection( anEnvironment, aURL )
53     local aSource = Source( aURL )
54     local aUser = aURL.user
55     local aPassword = aURL.password
56     local aHost = aURL.host
57     local aPort = aURL.port
58     local aConnection = assert( anEnvironment:connect( aSource, aUser, aPassword, aHost, aPort ) )
59    
60     return aConnection
61 end
62
63 function meta:__call( aURL, ... )
64     local URL = require( 'URL' )
65     local aURL = URL( aURL )
66     local anEnvironment = NewEnvironment( aURL, ... )
67     local aConnection = NewConnection( anEnvironment, aURL )
68     local aDB = { url = aURL, environment = anEnvironment, connection = aConnection }
69
70     setmetatable( aDB, self )
71    
72     return aDB
73 end
74
75 function self:__call( aStatement, ... )
76     return require( 'DBCursor' )( self, aStatement, ... )
77 end
78
79 function self:__newindex( aKey, aValue )
80     if aKey == 'autocommit' then
81         self.connection:setautocommit( aValue )
82     elseif aKey == 'commit' and aValue then
83         assert( self.connection:commit() )
84     elseif aKey == 'commit' and not aValue then
85         assert( self.connection:rollback() )
86     elseif aKey == 'close' and aValue then
87         self.connection:close()
88         self.environment:close()
89     end
90 end
91
92 function self:__concat( aValue )
93     return tostring( self ) .. tostring( aValue )
94 end
95
96 function self:__eq( aValue )
97     return tostring( self ) == tostring( aValue )
98 end
99
100 function self:__lt( aValue )
101     return tostring( self ) < tostring( aValue )
102 end
103
104 function self:__tostring()
105     return tostring( self.url )
106 end
107
108 --------------------------------------------------------------------------------
109 -- DBCursor
110 --------------------------------------------------------------------------------
111
112 module( 'DBCursor' )
113 _VERSION = '1.0'
114
115 local self = setmetatable( _M, {} )
116 local meta = getmetatable( self )
117
118 local function Escape( aValue )
119     return aValue:gsub( '\'', '\'\'' )
120 end
121
122 local function Encoder( aConnection )
123     if type( aConnection.escape ) == 'function' then
124         return function( aValue )
125             return aConnection:escape( aValue )
126         end
127     end
128
129     return Escape
130 end
131
132 local function BindValue( anEncoder, aValue )
133     local aType = type( aValue )
134    
135     if aType == 'string' then
136         aValue = ( '\'%s\'' ):format( anEncoder( aValue ) )
137     elseif aType == 'nil' then
138         aValue = 'null'
139     else
140         aValue = tostring( aValue )
141     end
142    
143     return aValue
144 end
145
146 local function BindStatement( aConnection, aStatement, ... )
147     local aStatement = tostring( aStatement )
148     local aCount =  select( '#', ... )
149    
150     if aCount > 0 then
151         local anEncoder = Encoder( aConnection )
152         local someBindings = {}
153        
154         for anIndex = 1, aCount do
155             local aValue = select( anIndex, ... )
156            
157             someBindings[ anIndex ] = BindValue( anEncoder, aValue )
158         end
159        
160         aStatement = aStatement:format( unpack( someBindings ) )
161     end
162
163     return aStatement
164 end
165
166 local function Normalize( someValues )
167     for anIndex, aValue in ipairs( someValues ) do
168         someValues[ anIndex ] = aValue:lower()
169     end
170    
171     return someValues
172 end
173
174 function meta:__call( aDB, aStatement, ... )
175     local aConnection = aDB.connection
176     local aStatement = BindStatement( aConnection, aStatement, ... )
177     local aResult = assert( aConnection:execute( aStatement ) )
178    
179     if type( aResult ) ~= 'number' then
180         local someNames = Normalize( aResult:getcolnames() )
181         local someTypes = Normalize( aResult:getcoltypes() )
182         local aCursor = { db = aDB, cursor = aResult, statement = aStatement, names = someNames, types = someTypes, row = {} }
183        
184         if aResult[ 'numrows' ] then
185             aCursor[ 'count' ] = aResult:numrows()
186         end
187        
188         setmetatable( aCursor, self )
189        
190         return aCursor
191     end
192    
193     return aResult
194 end
195
196 function self:__call()
197     local aCursor = self.cursor
198     local aRow = aCursor:fetch( self.row, 'n' )
199    
200     if aRow then
201         local someNames = self.names
202         local aCount = #someNames
203         local aResult = {}
204        
205         for anIndex = 1, aCount do
206             local aName = someNames[ anIndex ]
207             local aValue = aRow[ anIndex ]
208            
209             aResult[ aName ] = aValue
210         end
211        
212         return aResult
213     end
214    
215     aCursor:close()
216 end
217
218 function self:__newindex( aKey, aValue )
219     if aKey == 'close' and aValue then
220         self.cursor:close()
221     end
222 end
223
224 function self:__concat( aValue )
225     return tostring( self ) .. tostring( aValue )
226 end
227
228 function self:__eq( aValue )
229     return tostring( self ) == tostring( aValue )
230 end
231
232 function self:__lt( aValue )
233     return tostring( self ) < tostring( aValue )
234 end
235
236 function self:__tostring()
237     return tostring( self.statement )
238 end
Note: See TracBrowser for help on using the browser.